Java線程與Linux內核線程的映射關係

2020-03-15     sandag

Java 線程與 Linux 內核線程的映射關係 Linux 從內核2.6開始使用 NPTL (Native POSIX Thread Library) 支持,但這時線程本質上還輕量級進程。

Java 里的線程是由 JVM 來管理的,它如何對應到作業系統的線程是由 JVM 的實現來確定的。 Linux 2.6 上的 HotSpot 使用了 NPTL 機制, JVM線程跟內核輕量級進程有一一對應的關係 。線程的調度完全交給了作業系統內核,當然jvm還保留一些策略足以影響到其內部的線程調度,舉個例子,在linux下,只要一個 Thread.run 就會調用一個 fork 產生一個線程。

Java 線程在 Windows 及 Linux 平台上的實現方式,現在看來,是內核線程的實現方式。 這種方式實現的線程,是直接由作業系統內核支持的——由內核完成線程切換,內核通過操縱調度器(Thread Scheduler)實現線程調度,並將線程任務反映到各個處理器上。 內核線程是內核的一個分身。程序一般不直接使用該內核線程,而是使用其高級接口,即輕量級進程( LWP ),也即線程。這看起來可能很拗口。看圖:

(說明:KLT即內核線程 Kernel Thread ,是「內核分身」。每一個KLT對應到進程P中的某一個輕量級進程 LWP (也即線程),期間要經過用戶態、內核態的切換,並在 Thread Scheduler 下反應到處理器 CPU 上。)

​ 這種線程實現的方式也有它的缺陷:在程序面上使用內核線程,必然在作業系統上多次來回切換用戶態及內核態;另外,因為是一對一的線程模型, LWP 的支持數是有限的。

對於一個大型程序,我們可以 開闢的線程數量至少等於運行機器的cpu內核數量 。java程序里我們可以通過下面的一行代碼得到這個數量:

Runtime.getRuntime().availableProcessors();

所以最小線程數量即時 cpu 內核數量。如果所有的任務都是計算密集型的,這個最小線程數量就是我們需要的線程數。開闢更多的線程只會影響程序的性能,因為線程之間的切換工作,會消耗額外的資源。如果任務是IO密集型的任務,我們可以開闢更多的線程執行任務。當一個任務執行 IO 操作的時候,線程將會被阻塞,處理器立刻會切換到另外一個合適的線程去執行。如果我們只擁有與內核數量一樣多的線程,即使我們有任務要執行,他們也不能執行,因為處理器沒有可以用來調度的線程。

​ 如果線程有50%的時間被阻塞,線程的數量就應該是內核數量的2倍。 如果更少的比例被阻塞,那麼它們就是計算密集型的,則需要開闢較少的線程。如果有更多的時間被阻塞,那麼就是 IO 密集型的程序,則可以開闢更多的線程。於是我們可以得到下面的線程數量計算公式:線程數量=內核數量 / (1 – 阻塞率)

我們可以通過相應的分析工具或者 java 的 management 包來得到阻塞率的數值。

文章來源: https://twgreatdaily.com/zh-cn/5BH33nAB3uTiws8KeOLA.html