【Java】撩开Java线程的“神秘面纱”
文章目錄
- 一.用一句話講明白進程和線程的關系(不甚嚴密)
- 二.淺析進程與線程
- 三.多線程
- 四.Java中的線程是什么樣子的
- 五.java.lang.Thread & java.lang.Runnable
- Thread
- Runnable
- 使用run()方法——線程運行體
- 六.進程的生命周期(五狀態圖)
- 七.線程的生命周期
- Java線程的生命周期(五狀態圖)
- Java線程的生命周期(七狀態圖)
- 八.Java線程的主要API
- 九.Java線程的優先級
- 線程的優先級
- 表示線程優先級的常量
- 線程優先級方法
- 十.Java線程的啟動
- 十一.Java線程的休眠
- 十二.線程的終止
- 十三.Object類中線程的相關高級方法
- 十四.線程同步與synchronized
- synchronized與互斥鎖
- private與synchronized修飾的變量
- synchronized兩種同步方式的優缺點
- 深入分析synchronized
- 使用synchronized實現線程安全的懶漢式單例模式
- 十五.Java線程死鎖問題
- 十六.并發和并行的區別
- 十七.致謝
一.用一句話講明白進程和線程的關系(不甚嚴密)
操作系統可以同時執行多個任務,每個任務就是進程;一個進程可以同時執行多個任務,每個任務就是一個線程。
二.淺析進程與線程
每個正在系統上運行的程序都是一個進程。每個進程包含一到多個線程。進程也可能是整個程序或者是部分程序的動態執行。線程是一組指令的集合,或者是程序的特殊段,它可以在程序里獨立執行。也可以把它理解為代碼運行的上下文。所以線程基本上是輕量級的進程,它負責在單個程序里執行多任務。通常由操作系統負責多個線程的調度和執行。
線程是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以并發多個線程,每條線程并行執行不同的任務。在Unix System V及SunOS中也被稱為輕量進程,但輕量進程更多指內核線程,而把用戶線程稱為線程。
線程是獨立調度和分派的基本單位。線程可以為操作系統內核調度的內核線程,如Win32線程;由用戶進程自行調度的用戶線程,如Linux平臺的POSIX Thread;或者由內核與用戶進程,如Windows 7的線程,進行混合調度。
同一進程中的多條線程將共享該進程中的全部系統資源,如虛擬地址空間,文件描述符和信號處理等等。但同一進程中的多個線程有各自的調用棧,自己的寄存器環境,自己的線程本地存儲。
線程和進程的區別在于,子進程和父進程有不同的代碼和數據空間,而多個線程則共享數據空間,每個線程有自己的執行堆棧和程序計數器為其執行上下文.多線程主要是為了節約CPU時間,發揮利用,根據具體情況而定. 線程的運行中需要使用計算機的內存資源和CPU。
(這里基于Java講多線程,所以并沒有過于強調進程。其實,進程是操作系統的“主角”,對進程了解不足建議閱讀《操作系統概念》(《Operating System Concepts》),從理論上全面理解進程。)
三.多線程
多線程,是指從軟件或者硬件上實現多個線程并發執行的技術。具有多線程能力的計算機因有硬件支持而能夠在同一時間執行多于一個線程,進而提升整體處理性能。具有這種能力的系統包括對稱多處理機、多核心處理器以及芯片級多處理或同時多線程處理器。在一個程序中,這些獨立運行的程序片段叫作“線程”,利用它編程的概念就叫作“多線程處理”。
簡而言之,線程是程序中一個單一的順序控制流程。在單個程序中同時運行多個線程完成不同的工作,稱為多線程。
多線程編程的優點:
- 進程之間不易共享內存,但線程之間共享內存非常容易。
- 系統創建進程時需要為該進程重新分配系統資源,但創建線程的代價則小得多,因此使用多線程來實現多任務并發比多進程效率高。
- Java語言內置了多線程功能的支持,而不是單純地作為底層操作系統的調度方式,從而簡化了Java的多線程編程。
四.Java中的線程是什么樣子的
Java程序是通過線程執行的,線程在程序中具有獨立的執行路徑。當多條線程執行時,它們的路徑可以不同。
每一個Java應用程序都需要有一個執行main()函數的默認主線程。應用程序也可以創建線程在后臺操作時間密集型任務,以確保對用戶的響應。這些封裝了代碼執行序列的線程對象就被成為runnable。
JVM給了每個線程分配了獨立的JVM棧空間以免互相干擾。獨立的棧使得線程可以追蹤他們自己下一條將要執行的指令,這些指令會依線程的不同而有所區別。棧空間也為每條線程單獨準備了一份方法參數、局部變量以及返回值的拷貝。
Java主要是基于java.lang.Thread類以及java.lang.Runnable接口來實現基本的線程機制的。
Thread類為底層操作系統的線程體系架構提供一套統一的接口(通常OS負責創建和管理線程)。操作系統線程和Thread對象關聯(具體怎么關聯關系還要看OS的設計)。
Runnable接口則為關聯Thread對象的線程提供執行代碼。這些代碼放在Runnable的void run()方法中,這個方法雖然不接收任何參數且沒有返回值,但有可能拋出異常。
五.java.lang.Thread & java.lang.Runnable
Thread
這個類里面有大量不常見的關鍵詞:
- volatile
- native
- synchronized
Runnable
package java.lang;@FunctionalInterface public interface Runnable {public abstract void run(); }我們去掉注釋,不算空行,其實Runnable接口只有5行:
使用run()方法——線程運行體
要將一段代碼(線程體)在一個新的線程上運行,該代碼應該在一個線程類的run()函數中。
- 寫一個類implements Runnable接口,就必須重寫Runnable接口中的run()方法
- 寫一個類extends Thread類,就應該重寫Thread類的run()方法
六.進程的生命周期(五狀態圖)
下圖為進程的五狀態圖:
七.線程的生命周期
Java線程的生命周期(五狀態圖)
Java線程的生命周期(七狀態圖)
八.Java線程的主要API
| isAlive() | 判斷線程是否還未被終止 |
| getPriority() | 獲取線程優先級 |
| setPriority() | 設置線程優先級 |
| Thread.sleep() | 線程休眠 |
| join() | 等待調用線程運行的結束,再執行當前線程 |
| yield() | 讓出資源,進入就緒隊列等候 |
九.Java線程的優先級
線程的優先級
- Java提供一個線程調度器來監控程序中啟動后進入就緒狀態的所有線程,線程調度器按照線程的優先級來決定應調度哪個線程來執行
- Java線程的優先級用1~10的整數來表示,越小則優先級越低
- Java的優先級是高度依賴于操作系統的實現的
表示線程優先級的常量
Thread類有三個常量,表示常用的線程優先級:
- Thread.MIN_PRIORITY //1
- Thread.NORM_PRIORITY // 5
- Thread.MAX_PRIORITY // 10
線程默認是NORM_PRIORITY,優先級為5,包括main()線程。
線程優先級方法
- getPriority():確定線程的優先級
- setPriority():設置線程的優先級
十.Java線程的啟動
run()方法內是線程執行的代碼段,但一定要記住:不是用run()啟動線程!!!
應該用start()啟動!!!
十一.Java線程的休眠
與線程休眠有關的方法有三個:
- sleep()
- 讓線程中止一段時間的靜態方法
- Thread.sleep(long millis):暫時停止執行millis毫秒
- 在睡眠期滿的瞬間,再次調用該線程不一定會恢復它的執行,因為它很可能在等待隊列中
- join()
- 導致當前線程等待,直到調用這個 join 方法的線程終止
- join( )
- join(long millis)
- join(long millis,int nanos)
- yield()
- 為其他可運行的線程提供執行機會
- 靜態方法 — Thread.yield( )
十二.線程的終止
- 自動終止:一個線程完成執行后,不能再次運行
- 手動終止:
- stop():已過時,基本不用
- interrupt(): 粗暴的終止方式
- 可通過使用一個標志指示 run() 退出,從而終止線程
十三.Object類中線程的相關高級方法
- void wait():導致當前的線程等待,直到其他線程調用此對象的 notify()方法或 notifyAll() 方法
- void notify():喚醒在此對象監視器上等待的單個線程
- void notifyAll():喚醒在此對象監視器上等待的所有線程
十四.線程同步與synchronized
線程同步最簡單的方法是使用synchronized關鍵詞。
該關鍵詞可以用于修飾變量或者方法。
- synchronized修飾變量:synchronized (obj){// 需要被同步的代碼 }
- synchronized修飾方法:public synchronized void function(){//同步方法的內容 }
synchronized與互斥鎖
在Java語言中,引入了對象互斥鎖的概念,來保證共享數據操作的完整性。
- 每個對象都對應于一個可稱為“互斥鎖”的標記,這個標記用來保證在任一時刻,只能有一個線程訪問該對象
- 關鍵字synchronized來與對象的互斥鎖聯系。當某個對象用synchronized修飾時,表明該對象在任一時刻只能由一個線程訪問
- 同步的局限性:導致程序的執行效率要降低
- 同步方法(非靜態的)的鎖為this
- 同步方法(靜態的)的鎖為當前類本身
private與synchronized修飾的變量
受到synchronized保護的程序代碼塊和方法中,要訪問的對象屬性必須設定為private,因為如果不設定為private,那么就可以用不同的方式來訪問它,這樣就達不到保護的效果了。
synchronized兩種同步方式的優缺點
-
synchronized方法
優點:- 可以顯示的知道哪些方法是被synchronized關鍵字保護的
缺點:
- 方法中有些內容是不需要同步的,如果該方法執行會花很長時間,那么其他人就要花較多時間等待鎖被歸還
- 只能取得自己對象的鎖,有時候程序設計的需求,可能會需要取得其他對象的鎖
-
synchronized代碼塊
優點:- 可以針對某段程序代碼同步,不需要浪費時間在別的程序代碼上
- 可以取得不同對象的鎖
缺點:
- 無法顯示的得知哪些方法是被synchronized關鍵字保護的
深入分析synchronized
synchronized關鍵詞實際上使用管程ADT實現,原先效率很低,Java的幾個重要版本中經歷不斷優化已經好很多了,但還是慢。
個人覺得大家可以這么去理解synchronized:所謂多線程程序,其實是巧妙地利用了多核系統的并發并行,并發與并行對其很重要。但是,synchronized與所謂管程(操作系統的概念),正是對這里開刀,它不給你控制的機會,直接保證臨界區(操作系統的概念)代碼段的執行從并發、并行到串行,一次只能走一條線程。它相當于一種悲觀鎖,直接粗暴地化并發、并行為串行,保證了并發程序的同步與共享數據一致性。
使用synchronized實現線程安全的懶漢式單例模式
public class Singleton {private static Singleton instance = null;private Singleton() {} public static Singleton getInstance() { if(instance == null) { synchronized(Singleton.class) { if(instance == null) { instance = new Singleton(); } } } return instance; } }測試類:
public class SingletonTest { public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1==s2); } }十五.Java線程死鎖問題
(不規范解釋) 死鎖:兩個線程,彼此在等待對方釋放其持有的鎖,而陷入無線等待。
鎖的歸還幾種方式:
- 基本上執行完同步的程序代碼后,鎖就會自動歸還
- 用break語句跳出同步的語句塊,不過這對于寫在方法聲明的 synchronized沒有作用
- 遇到return語句
- 遇到異常
十六.并發和并行的區別
薦讀:《并發和并行的區別》
十七.致謝
- 感謝@碼農云帆哥的指正,已改
總結
以上是生活随笔為你收集整理的【Java】撩开Java线程的“神秘面纱”的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 通过“FBI树”复习二叉树算法(洛谷P1
- 下一篇: 【计算机组成原理】Chapter1-复习