前言
線程是比進程更輕量級的調(diào)度執(zhí)行單位,線程的引入,可以把一個進程的資源分配和執(zhí)行調(diào)度分開,各個線程既可以共享進程資源(內(nèi)存地址、文件I/O等),又可以獨立調(diào)度(線程是CPU調(diào)度的基本單位)。JAVA語言中每個已經(jīng)執(zhí)行了 start() 且還未結(jié)束的 java.lang.Thread 類的實例就代表了一個線程。Thread 類的所有關(guān)鍵方法都是聲明為Native的,在Java API中,一個Native方法往往意味著這個方法沒有使用或者無法使用平臺無關(guān)的手段來實現(xiàn)(也可能為了執(zhí)行效率)。
1. 線程的實現(xiàn)
實現(xiàn)線程主要有3種方法:內(nèi)核線程、用戶線程、用戶線程加輕量級進程混合實現(xiàn)。
1.1 使用內(nèi)核線程實現(xiàn)
內(nèi)核線程(Kernel-Level Thread, KLT)就是由操作系統(tǒng)內(nèi)核支持的線程,內(nèi)核通過操作調(diào)度器(Scheduler)對線程進行調(diào)度。
程序一般不會直接使用內(nèi)核線程,而是去使用內(nèi)核線程的一種高級接口-輕量級進程(Light Weight Process, LWP),輕量級進程就是我們通常意義上所講的線程。
這種輕量級進程與內(nèi)核線程之間1:1的關(guān)系稱為一對一的線程模型,如下圖。
優(yōu)點: 每個輕量級進程都成為一個獨立的調(diào)度單元,即使有一個輕量級進程在系統(tǒng)中阻塞了,也不會影響整個進程繼續(xù)工作。缺點: 由于是基于內(nèi)核線程實現(xiàn)的,所以各種線程操作,如創(chuàng)建、析構(gòu)及同步,都需要進行系統(tǒng)調(diào)用,需要在用戶態(tài)和內(nèi)核態(tài)中來回切換,代價相對較高。
1.2 使用用戶線程實現(xiàn)
用戶線程(User Thread, UT)指的是完全建立在用戶空間的線程庫上,系統(tǒng)內(nèi)核不能感知線程存在的實現(xiàn)。
用戶線程的建立、同步、銷毀和調(diào)度完全在用戶態(tài)中完成,不需要內(nèi)核的幫助。因此操作比內(nèi)核線程更快速,并可以支持更大的線程數(shù)量。
這種進程(不是輕量級進程)與用戶線程之間1:N的關(guān)系稱為一對多的線程模型。
優(yōu)點: 線程所有操作都在用戶態(tài)完成,不需要切換到內(nèi)核態(tài),如果實現(xiàn)得當,那么速度非常快,資源消耗也很低,可以支持更大規(guī)模的線程數(shù)量。
缺點: 由于線程的操作沒有操作系統(tǒng)內(nèi)核的支援,所有線程操作都需要用戶程序自己實現(xiàn),創(chuàng)建、線程間的切換、調(diào)度都需要用戶程序處理,并且由于操作系統(tǒng)只把處理器資源分配到了進程,除了線程切換獲取處理器外,線程阻塞如何處理,或者把進程內(nèi)的線程映射到其他處理器,都是非常困難的。
現(xiàn)在使用用戶線程的程序越來越少了,像Java、Ruby等語言都曾經(jīng)使用過用戶線程,不過現(xiàn)在都放棄了。
1.3 使用用戶線程加輕量級進程混合實現(xiàn)
在這種混合實現(xiàn)下,既存在用戶線程,也存在輕量級進程。
用戶線程還是完全建立在用戶空間中,因此用戶線程的創(chuàng)建、切換、析構(gòu)等操作依然廉價。而操作系統(tǒng)提供輕量級進程則作為用戶線程和內(nèi)核線程之間的橋梁。這樣可以使用內(nèi)核提供的線程調(diào)度功能及處理器映射,用戶線程調(diào)用通過輕量級線程來完成。
用戶線程與輕量級進程的數(shù)量比是不定的,即為N:M的關(guān)系,這種就是多對多的線程模型。
2. Java 線程的實現(xiàn)
Java 線程在JDK 1.2之前,是基于用戶線程實現(xiàn)的,JDK 1.2中替換為基于操作系統(tǒng)原生線程模型來實現(xiàn)。目前的JDK版本中,操作系統(tǒng)支持怎樣的線程模型,很大程序上決定了Java虛擬機的線程是怎樣映射的。
虛擬機規(guī)范中并未限定Java線程需要使用哪種線程模型來實現(xiàn)。
對于Sun JDK來說,它的windows版與linux版都是使用一對一的線程模型實現(xiàn)的,一條Java線程映射到一條輕量級進程中,即內(nèi)核線程的實現(xiàn)方式。
3. Java 線程的調(diào)度
線程調(diào)度是指系統(tǒng)為線程分配處理器使用權(quán)的過程。主要有協(xié)同式線程調(diào)度和搶占式線程調(diào)度兩種。
如果使用協(xié)同式調(diào)度的多線程系統(tǒng),線程的執(zhí)行時間由線程本身來控制,線程把自己的工作執(zhí)行完了之后,要主動通知系統(tǒng)切換到另外一個線程上。如果一個線程寫得有問題,一直不告知系統(tǒng)進行線程切換,那么程序就會一直阻塞在那里。
如果使用搶占式調(diào)度的多線程系統(tǒng),那么每個線程將由系統(tǒng)來分配執(zhí)行時間,線程的切換不由線程本身來決定。
Java 使用的線程調(diào)度方法就是搶占式調(diào)度。