多執行緒:執行緒同步的幾種方式
1.synchronized同步方法 (靜態方法鎖住類物件,其它方法鎖住例項物件)
即有synchronized關鍵字修飾的方法。
由於java的每個物件都有一個內建鎖,當用此關鍵字修飾方法時,
內建鎖會保護整個方法。在呼叫該方法前,需要獲得內建鎖,否則就處於阻塞狀態。
2.synchronized同步程式碼塊 (鎖住標記的物件)
即有synchronized關鍵字修飾的語句塊。
被該關鍵字修飾的語句塊會自動被加上內建鎖,從而實現同步
3.使用ReentrantLock實現執行緒同步
在JavaSE5.0中新增了一個java.util.concurrent包來支援同步。ReentrantLock類是可重入、互斥、實現了Lock介面的鎖, 它與使用synchronized方法和快具有相同的基本行為和語義,並且擴充套件了其能力。
ReenreantLock類的常用方法有:
ReentrantLock() : 建立一個ReentrantLock例項
lock() : 獲得鎖
程式碼
unlock() : 釋放鎖
注:ReentrantLock()還有一個可以建立公平鎖的構造方法,但由於能大幅度降低程式執行效率,不推薦使用
4.使用特殊域變數(volatile)實現執行緒同步(無法保證原子性的,寫到這兒來只是強調一下,它只保證了可見性和有序性。)
a.volatile關鍵字為域變數的訪問提供了一種免鎖機制,
b.使用volatile修飾域相當於告訴虛擬機器該域可能會被其他執行緒更新,
c.因此每次使用該域就要重新計算,而不是使用暫存器中的值
d.volatile不會提供任何原子操作,它也不能用來修飾final型別的變數
e.
對於volatile修飾的變數,jvm虛擬機器只是保證從主記憶體載入到執行緒工作記憶體的值是最新的。
您只能在有限的一些情形下使用 volatile 變數替代鎖。要使 volatile 變數提供理想的執行緒安全,必須同時滿足下面兩個條件:
· 對變數的寫操作不依賴於當前值。
· 該變數沒有包含在具有其他變數的不變式中。
可以使用的場景:
將volatile 變數作為狀態標誌使用(和lock差不多,但是volatile賦值true本來這種就是原子操作,所以就剛好可以使用。)
5.volatile(保證可見性和有序性)的一個重要作用就是和CAS結合,保證了原子性
最簡單的比如i++,用volatile可以保證取得的值是最新的,而cas操作可以保證你修改前後的值只+1,而不會覆蓋掉別的執行緒已經修改過的值,如果別的執行緒已經修改過,CAS會自動不修改的。
但是沒有volatile時,原子性不保證可見性。
CAS改完可能值還在快取裡,不會馬上把工作記憶體中被修改後的值 寫回 主記憶體。
CAS只解決了比較和更新的原子性的問題,要保證可見性,需要加鎖或者是用volatile修飾變數。
1. 首先,宣告共享變數為volatile;
2. 然後,使用CAS的原子條件更新來實現執行緒之間的同步;
3. 同時,配合以volatile的讀/寫和CAS所具有的volatile讀和寫的記憶體語義來實現執行緒之間的通訊。
一句話:volatile變數的讀/寫和CAS可以實現執行緒之間的通訊。把這些特性整合在一起,就形成了整個concurrent包得以實現的基石。