JDK19推出了幾個新的特性,其中最具有特點的有以下幾個。
圖片
我們看到其中有一個比較值得關注的那就是新增了虛擬線程。到底什么是虛擬線程,和我們現在使用的線程有啥區別呢?
在操作系統中,線程是比進程更輕量級的調度執行單位,線程的引入可以把一個進程的資源分配和執行調度分開,各個線程既可以共享進程資源,又可以獨立調度。
線程的實現方式主要有三種:分別是使用內核線程實現、使用用戶線程實現以及使用用戶線程加輕量級進程混合實現。
「使用內核線程實現」:內核線程(Kernel-Level Thread,KLT)直接由操作系統內核支持,由內核完成線程切換,內核通過操縱調度器對線程進行調度,并負責將線程的任務映射到各個處理器上,同時向應用程序提供API接口來管理線程。應用程序一般不直接使用內核線程,而是使用內核線程的一種高級接口——輕量級進程(Light Weight Process,LWP)。
應用程序一般不會直接去使用內核線程,而是去使用內核線程的一種高級接口——輕量級進程(Light Weight Process,LWP),輕量級進程就是我們通常意義上所講的線程,由于每個輕量級進程都由一個內核線程支持,因此只有先支持內核線程,才能有輕量級進程。
有了內核線程的支持,每個輕量級進程都成為一個獨立的調度單元,即使有一個輕量級進程在系統調用中阻塞了,也不會影響整個進程繼續工作。
但是輕量級進程具有它的局限性:首先,由于是基于內核線程實現的,所以各種線程操作,如創建、析構及同步,都需要進行系統調用。而系統調用的代價相對較高,需要在用戶態(User Mode)和內核態(Kernel Mode)中來回切換。其次,每個輕量級進程都需要有一個內核線程的支持,因此輕量級進程要消耗一定的內核資源(如內核線程的棧空間),因此一個系統支持輕量級進程的數量是有限的。
「使用用戶線程實現」:在用戶空間建立線程庫,通過運行時系統完成線程的管理。這種實現方式下,一個進程和線程之間是一對多的關系。其優點是線程切換快,并且可以運行在任何操作系統之上,只需要實現線程庫即可。
這種實現方式下,一個進程和線程之間的關系是一對多的。
這種線程實現方式的優點是線程切換快,并且可以運行在任何操作系統之上,只需要實現線程庫就行了。但是缺點也比較明顯,就是所有線程的操作都需要用戶程序自己處理,并且因為大多數系統調用都是阻塞的,所以一旦一個進程阻塞了,那么進程中的所有線程也會被阻塞。還有就是多處理器系統中如何將線程映射到其他處理器上也是一個比較大的問題。
「使用用戶線程加輕量級進程混合實現」:這種實現方式結合了上述兩種方式的優點,既具有用戶線程實現方式的線程切換快的優點,也具有內核線程實現方式的操作系統支持性好的優點。
「Java線程的實現方式」Java作為一門跨平臺的編程語言,實際上他的線程的實現其實是依賴具體的操作系統的。而比較常用的windows和linux來說,都是采用內核線程的方式實現的。也就是說,當我們在JAVA代碼中創建一個Thread的時候,其實是需要映射到操作系統的線程的具體實現的,因為常見的通過內核線程實現的方式在創建、調度時都需要進行內核參與,所以成本比較高,盡管JAVA中提供了線程池的方式來避免重復創建線程,但是依舊有很大的優化空間。而且這種實現方式意味著受機器資源的影響,平臺線程數也是有限制的。
JDK19引入的虛擬線程,虛擬線程是輕量級線程,主要用于實現高吞吐量的并發應用程序。與傳統的線程相比,虛擬線程具有更低的創建和銷毀成本,可以更好地利用系統資源。
圖片
虛擬線程的主要優勢:
「虛擬線程與平臺線程的區別」:
「如何使用虛擬線程」:通過Thread.startVirtualThread()可以運行一個虛擬線程:
Thread.startVirtualThread(() -> { System.out.println("虛擬線程執行中...");});
通過Thread.Builder也可以創建虛擬線程,Thread類提供了ofPlatform()來創建一個平臺線程、ofVirtual()來創建虛擬現場。
Thread.Builder platformBuilder = Thread.ofPlatform().name("平臺線程");Thread.Builder virtualBuilder = Thread.ofVirtual().name("虛擬線程");Thread t1 = platformBuilder .start(() -> {...}); Thread t2 = virtualBuilder.start(() -> {...});
線程池也支持了虛擬線程,可以通過Executors.newVirtualThreadPerTaskExecutor()來創建虛擬線程:
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { IntStream.range(0, 10000).forEach(i -> { executor.submit(() -> { Thread.sleep(Duration.ofSeconds(1)); return i; }); });}
其實并不建議虛擬線程和線程池一起使用,因為Java線程池的設計是為了避免創建新的操作系統線程的開銷,但是創建虛擬線程的開銷并不大,所以其實沒必要放到線程池中。
「性能差異」從Runnable創建10000個線程,并使用虛擬線程和平臺線程執行它們,以比較兩者的性能。
final AtomicInteger atomicInteger = new AtomicInteger();Runnable runnable = () -> { try { Thread.sleep(Duration.ofSeconds(1)); } catch(Exception e) { System.out.println(e); } System.out.println("Work Done - " + atomicInteger.incrementAndGet());};
傳統線程實現:
Instant start = Instant.now();try (var executor = Executors.newFixedThreadPool(100)) { for(int i = 0; i < 10_000; i++) { executor.submit(runnable); }}Instant finish = Instant.now();long timeElapsed = Duration.between(start, finish).toMillis(); System.out.println("總耗時 : " + timeElapsed);
虛擬線程實現:
Instant start = Instant.now();try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { for(int i = 0; i < 10_000; i++) { executor.submit(runnable); }}Instant finish = Instant.now();long timeElapsed = Duration.between(start, finish).toMillis(); System.out.println("總耗時 : " + timeElapsed);
最終結果:
總耗時 : 102323總耗時 : 1674
100秒和1.6秒的差距,虛擬線程的性能提升還是比較大的。
Java虛擬線程相對于傳統線程具有更好的性能表現,特別是在高并發場景下。但是需要注意的是,虛擬線程并不是萬能的,過度使用虛擬線程也可能會導致其他問題,如線程數量過多、線程間通信和同步問題等。
本文鏈接:http://www.www897cc.com/showinfo-26-66350-0.htmlJDK19推出新特性虛擬線程是什么?
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 微信 12 月打擊處理 3555 篇不實文章,十大謠言包括“網傳廣州限制外賣配送”
下一篇: 查電影評分上互聯網?別逗了!