在別的語(yǔ)言比如go都帶來(lái)了協(xié)程或者有async/await來(lái)優(yōu)化異步編程之后,Java也感受到了壓力,畢竟當(dāng)下純計(jì)算的應(yīng)用非常少,大多還是有IO阻塞的,這樣的話同步編程就需要大量的線程來(lái)充分利用CPU提升性能和保持更多的連接數(shù)。對(duì)于操作系統(tǒng)來(lái)說(shuō),創(chuàng)建一個(gè)線程也需要做很多工作(分配線程棧,維護(hù)線程狀態(tài)以及調(diào)度等),這會(huì)導(dǎo)致IO密集型應(yīng)用浪費(fèi)大量的時(shí)間在線程切換上,于是人們更傾向于使用異步編程來(lái)提高系統(tǒng)性能(比如Javascript/Nodejs)。異步編程的一個(gè)缺點(diǎn)就是回調(diào)太多,這時(shí)聰明的人們使用sync/await來(lái)優(yōu)化編碼體驗(yàn)。線程問(wèn)題的另外一個(gè)解法就是用戶態(tài)線程,此時(shí)線程不是操作系統(tǒng)維護(hù)而是程序自己維護(hù),應(yīng)用程序自己向操作系統(tǒng)申請(qǐng)少量的線程(通常是CPU的核數(shù)),然后再基于操作系統(tǒng)線程來(lái)執(zhí)行用戶態(tài)線程。這和線程池的區(qū)別就是用戶態(tài)線程任務(wù)遇到阻塞時(shí),操作系統(tǒng)線程可以繼續(xù)執(zhí)行其他任務(wù),只是將用戶態(tài)線程掛起。
Java19已結(jié)帶來(lái)了用戶態(tài)線程的特性,目前處于實(shí)驗(yàn)狀態(tài),我們也可以測(cè)試一下了。之所以Java選擇用戶態(tài)線程而不是異步模式,那是因?yàn)橛脩魬B(tài)線程可以兼容已有的多線程代碼。
使用
當(dāng)前Java19為了能編譯如下的代碼,需要在IDEA中設(shè)置編譯參數(shù)--enable-preview

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);
}
});
運(yùn)行它控制臺(tái)會(huì)輸出線程名字為v1,然后打開jconsole,并沒(méi)有看的名字為v1的線程,說(shuō)明確實(shí)沒(méi)有創(chuàng)建操作系統(tǒng)級(jí)的線程。
接下來(lái)新建一個(gè)虛擬線程的線程池
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進(jìn)程的系統(tǒng)線程,并無(wú)線程池的線程,而只有ForkJoinPool線程。
這里只是舉一個(gè)例子,不打算測(cè)試極限性能比系統(tǒng)級(jí)線程優(yōu)秀多少,感興趣的可以自己試試。
還有需要討論一下使用虛擬線程了還有無(wú)使用線程池的必要性,我們知道線程池除了復(fù)用線程避免重復(fù)創(chuàng)建線程之外還有基于隊(duì)列的限流和調(diào)峰的能力,使用線程池還是能夠得到好處的。此時(shí)我們的虛擬線程消耗資源更少那么可以創(chuàng)建更多的虛擬線程(即線程池的PoolSize可以設(shè)置大一些)。