線程(thread) 是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以并發多個線程,每條線程并行執行不同的任務。
一、線程的分類
我們知道計算機可以分為硬件和軟件兩大塊,硬件是基礎,軟件提供實現不同功能的手段;而軟件又可以分為操作系統和應用程序,操作系統專注于對硬件的交互管理并提供一個運行環境給應用程序使用,應用程序則是能實現若干功能的并且運行在操作系統環境中的軟件。
同樣,線程按照操作系統和應用程序兩層次可以分為內核線程(Kernel-Level Thread)和用戶線程(User Thread)。
- 內核線程(Kernel-Level Thread,KLT) 就是直接由操作系統內核(Kernel,下稱內核)支持的線程,這種線程由內核來完成線程切換
- 用戶線程(User Thread,UT) 從廣義上來講,一個線程只要不是內核線程,就可以認為是用戶線程;而狹義上的用戶線程指的是完全建立在用戶空間的線程庫上,系統內核不感知線程存在的實現。用戶線程的建立、同步、銷毀和調度完全在用戶態中完成,不需要內核的幫助
二、Java線程的實現
Java創建線程的兩種方式:實現Runnable接口,繼承Thread類
- 實現Runnable接口:寫一個類實現Runnable接口,實現里面的run方法,用new Thread(Runnable target).start()方法來啟動
- 增強了程序的健壯性,代碼能夠被多個線程共享,代碼與數據是獨立的
- 線程體run()方法所在的類還可以從其他類繼承一些有用的屬性和方法,避免了由于Java的單繼承特性帶來的局限
- 有利于保持程序風格的一致性
- 繼承Thread類:寫一個類繼承自Thread類,然后重寫里面的run方法,用start方法啟動線程
- Java中只支持單繼承,Thread子類無法再從其他類繼承
- 編寫簡單,run()方法的當前對象就是線程對象,可直接操縱
三、Java的線程優先級
Java使用的線程調度方式就是搶占式調度
雖然Java線程調度是系統自動完成的,但是我們還是可以“建議”系統給某些線程多分配一點執行時間,另外的一些線程則可以少分配一點——這項操作可以通過設置線程優先級來完成。
Java語言一共設置了10個級別的線程優先級(Thread.MIN_PRIORITY至Thread.MAX_PRIORITY),在兩個線程同時處于Ready狀態時,優先級越高的線程越容易被系統選擇執行。不過,線程優先級并不是太靠譜,原因是Java的線程是通過映射到系統的原生線程上來實現的,所以線程調度最終還是取決于操作系統,雖然現在很多操作系統都提供線程優先級的概念,但是并不見得能與Java線程的優先級一一對應,如Solaris中有2147483648(232)種優先級,但Windows中就只有7種,比Java線程優先級多的系統還好說,中間留下一點空位就可以了,但比Java線程優先級少的系統,就不得不出現幾個優先級相同的情況了
下圖顯示了Java線程優先級與Windows線程優先級之間的對應關系,Windows平臺的JDK中使用了除THREAD_PRIORITY_IDLE之外的其余6種線程優先級。
Java線程優先級 | Windows線程優先級 |
---|---|
1. Thread.MIN_PRIORITY | THREAD_PRIORITY_LOWEST |
2. | THREAD_PRIORITY_LOWEST |
3. | THREAD_PRIORITY_BELOW_NORMAL |
4. | THREAD_PRIORITY_BELOW_NORMAL |
5. Thread.NORM_PRIORITY | THREAD_PRIORITY_NORMAL |
6. | THREAD_PRIORITY_ABOVE_NORMAL |
7. | THREAD_PRIORITY_ABOVE_NORMAL |
8. | THREAD_PRIORITY_HIGHEST |
9. | THREAD_PRIORITY_HIGHEST |
10. Thread.MAX_PRIORITY | THREAD_PRIORITY_CRITICAL |
其實,即使設置了線程的優先級,一樣無法確保這個線程一定先執行,因為它有很大的隨機性。它并無法控制執行哪個線程,因為線程的執行,是搶占資源后才能執行的操作,而搶占到資源時,最多是給于線程優先級較高的線程一點機會而已,能不能抓住可是不一定的。
說到底就一句話:線程優化級較高的線程不一定先執行
四、Java線程生命周期
線程的生命周期包含5個階段,包括:新建、就緒、運行、阻塞、銷毀。
- 新建:就是剛使用new方法,new出來的線程;
- 就緒:就是調用的線程的start()方法后,這時候線程處于等待CPU分配資源階段,誰先搶的CPU資源,誰開始執行;
- 運行:當就緒的線程被調度并獲得CPU資源時,便進入運行狀態,run方法定義了線程的操作和功能;
- 阻塞:在運行狀態的時候,可能因為某些原因導致運行狀態的線程變成了阻塞狀態,比如sleep()、wait()之后線程就處于了阻塞狀態,這個時候需要其他機制將處于阻塞狀態的線程喚醒,比如調用notify或者notifyAll()方法。喚醒的線程不會立刻執行run方法,它們要再次等待CPU分配資源進入運行狀態;
- 銷毀:如果線程正常執行完畢后或線程被提前強制性的終止或出現異常導致結束,那么線程就要被銷毀,釋放資源;
完整的生命周期圖如下:
?