Java19新实验性特性,虚拟线程

在别的语言比如go都带来了协程或者有async/await来优化异步编程之后,Java也感受到了压力,毕竟当下纯计算的应用非常少,大多还是有IO阻塞的,这样的话同步编程就需要大量的线程来充分利用CPU提升性能和保持更多的连接数。对于操作系统来说,创建一个线程也需要做很多工作(分配线程栈,维护线程状态以及调度等),这会导致IO密集型应用浪费大量的时间在线程切换上,于是人们更倾向于使用异步编程来提高系统性能(比如Javascript/Nodejs)。异步编程的一个缺点就是回调太多,这时聪明的人们使用sync/await来优化编码体验。线程问题的另外一个解法就是用户态线程,此时线程不是操作系统维护而是程序自己维护,应用程序自己向操作系统申请少量的线程(通常是CPU的核数),然后再基于操作系统线程来执行用户态线程。这和线程池的区别就是用户态线程任务遇到阻塞时,操作系统线程可以继续执行其他任务,只是将用户态线程挂起。

Java19已结带来了用户态线程的特性,目前处于实验状态,我们也可以测试一下了。之所以Java选择用户态线程而不是异步模式,那是因为用户态线程可以兼容已有的多线程代码。

使用

当前Java19为了能编译如下的代码,需要在IDEA中设置编译参数--enable-preview


image.png
Thread.ofVirtual().name("v1").start(() -> {
            System.out.println("ofVirtual");
            try {
                Thread.sleep(2222);
                System.out.println("Thread: " + Thread.currentThread().getName());
                while (true) {
                    System.out.println("2222");
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });

运行它控制台会输出线程名字为v1,然后打开jconsole,并没有看的名字为v1的线程,说明确实没有创建操作系统级的线程。

接下来新建一个虚拟线程的线程池

ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 200, 60, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(100), r -> Thread.ofVirtual().unstarted(r),
                new ThreadPoolExecutor.DiscardOldestPolicy());
        final AtomicInteger count = new AtomicInteger();
Stream.generate(count::getAndIncrement).limit(Integer.MAX_VALUE).forEach(c -> executor.submit(() -> {
            System.out.println("task " + c);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }));

同样查看该Java进程的系统线程,并无线程池的线程,而只有ForkJoinPool线程。
这里只是举一个例子,不打算测试极限性能比系统级线程优秀多少,感兴趣的可以自己试试。

还有需要讨论一下使用虚拟线程了还有无使用线程池的必要性,我们知道线程池除了复用线程避免重复创建线程之外还有基于队列的限流和调峰的能力,使用线程池还是能够得到好处的。此时我们的虚拟线程消耗资源更少那么可以创建更多的虚拟线程(即线程池的PoolSize可以设置大一些)。

你可能感兴趣的:(Java19新实验性特性,虚拟线程)