淺談Java併發

全棧黑科技發表於2023-01-02

Java併發是比較難的知識點,難於對併發的理解。併發要從作業系統和硬體層面去理解,才會比較深入,而不單單是從程式語言的邏輯去理解。

首先對於併發要清楚的幾點:

執行緒可能在任何時刻被切換。
計算機只對硬體指令保證原子性。
CPU有多級快取,每個執行緒有自己的快取空間。

第一點:

如果有共享資料或多執行緒判斷邏輯,需要使用鎖機制互斥其他執行緒的訪問,避免邏輯錯誤。

第二點:

例如下面這個語句在程式語言裡面只有一條語句,但編譯成組合語言會有三條硬體指令:(1)load i變數,(2)i變數自增1 (3)儲存自增後的i變數。因此下面的語句不具有原子性。
i++;
第三點:
造成變數的可見性問題,單個執行緒讀取並修改,修改的是自己執行緒快取裡面變數,對其他執行緒不可見,會造成可見性問題。

Java解決併發問題的一些關鍵字
1.synchronized關鍵字
監視器模式的鎖,可以使程式碼塊線上程之間互斥,持有鎖的執行緒可以執行,沒有持有鎖的執行緒進入阻塞佇列等待。synchronized是可重入鎖,持有鎖的執行緒可多次進入程式碼塊。synchronized關鍵字修飾的程式碼塊/方法內的程式碼具有原子性、可見性,可以解決上面的1、2、3問題。
2.volatile關鍵字
修飾變數,保證了變數的可見性,修飾的變數不再被CPU快取,每次讀取從記憶體中讀取。每次寫入變數也會立即重新整理到記憶體,以保證變數對其他執行緒可見。還可以避免指令重排序,以及避免字分裂( 64位變數指令操作的分為兩步)。但是這個不保證原子性。可解決上面的問題3。
3.final關鍵字
保證變數的不變性,如果一個變數是不變的,那麼這個變數一定是執行緒安全的。
需要注意的是JVM為了效能最佳化,會對編譯後的指令進行重排序,即在單個執行緒內是無影響的,但是對於多個執行緒來看重排序可能會導致預期之外的錯誤。volatile可以避免指令重排序。
在Java中的多執行緒是一一對映到作業系統的核心執行緒,每次執行緒切換都要經過核心態,對效能影響較大。
在Java中執行緒對應是的Thread類

Thread類透過接收一個Runnable介面的run方法來確定執行緒的執行邏輯。

Thread類透過start()方法來啟動執行緒,裡面執行的是定義好的run方法,start()方法只能執行一次,否則會報IllegalThreadStateException。

Thread thread = new Thread(() -> {
System.out.println(“hello world”);
});
thread.start();
Runnable介面是一個函式式介面,可以使用lambda表示式。

Thread類的其他方法介紹

1.sleep()方法

讓目前正在執行的執行緒休眠,讓CPU去執行其他的任務。

2.join()方法

執行緒合併,即當前執行緒會阻塞呼叫join()的執行緒,直到join的執行緒執行完成或超出設定的等待時間。

3.yield()方法

執行緒的yield(讓步)操作的作用是讓目前正在執行的執行緒放棄當前的執行,讓出CPU的執行許可權,使得CPU去執行其他的執行緒。

執行緒的狀態

1.NEW狀態

​new Thread()建立了執行緒,但未呼叫start()啟動執行緒。

2.RUNNABLE狀態

Java把Ready(就緒)和Running(執行)兩種狀態合併為一種狀態:RUNNABLE(可執行)狀態(或者可執行狀態)。呼叫了執行緒的start()例項方法後,執行緒就處於就緒狀態。此執行緒獲取到CPU時間片後,開始執行run()方法中的業務程式碼,執行緒處於執行狀態。

3.BLOCKED狀態處於BLOCKED(阻塞)狀態的執行緒並不會佔用CPU資源,以下情況會讓執行緒進入阻塞狀態:

(1)執行緒等待獲取鎖

(2)IO阻塞

4.WAITING狀態

處於WAITING(無限期等待)狀態的執行緒不會被分配CPU時間片,需要被其他執行緒顯式地喚醒,才會進入就緒狀態。

Object.wait()方法,對應的喚醒方式為:Object.notify()/Object.notifyAll()。

Thread.join()方法,對應的喚醒方式為:被合入的執行緒執行完畢。LockSupport.park()方法,對應的喚醒方式為:LockSupport.unpark(Thread)。

5.TIMED_WAITING狀態
處於TIMED_WAITING(限時等待)狀態的執行緒不會被分配CPU時間片,如果指定時間之內沒有被喚醒,限時等待的執行緒會被系統自動喚醒,進入就緒狀態。
6.TERMINATED狀態執行緒結束任務之後,將會正常進入TERMINATED(死亡)狀態;或者說線上程執行過程中發生了異常(而沒有被處理),也會導致執行緒進入死亡狀態。

公眾號:

 


推薦閱讀:
《Java併發程式設計實踐》

相關文章