1. 程式人生 > >多執行緒經常使用的3個關鍵字:synchronized、ReentrantLock、volatile

多執行緒經常使用的3個關鍵字:synchronized、ReentrantLock、volatile

3個關鍵字synchronized、ReentrantLock、volatile。

一、synchronized

  互斥鎖,即操作互斥,併發執行緒過來,序列獲得鎖,序列執行程式碼。就像一個房間一把鑰匙,一個人進去後,下一個人得等第一個人出來得到鑰匙才能進入。如果程式碼寫的不好(A),可能出現死鎖!(A得到鎖,B等待A釋放鎖,A不釋放,B死鎖)

示例程式碼:

複製程式碼
   //修飾靜態方法:類級別互斥(只要是房子此方法就互斥)
    public synchronized static void foo1(){
        //do 
    }
    
    //修飾普通方法:物件級別互斥(只有同一間房子此方法互斥)
public synchronized void foo2(){ //do } //修飾程式碼塊 public void foo3(){ //類級別互斥 synchronized(DennyTest.class) { //do } } //修飾程式碼塊 public void foo4(){ //物件級別互斥 synchronized(this) { //do } }
複製程式碼

需要注意的是,經常使用的是物件級別的互斥,那麼特別需要注意是同一個物件的鎖。新手經常犯錯,都不是同一個物件,當然鎖不住。

二、ReentrantLock

可重入鎖,和同步鎖功能類似,不過需要顯示的建立、銷燬。特點:

1.ReentrantLock有tryLock方法,如果鎖被其他執行緒持有,返回false,可避免形成死鎖。

2.建立時可自定義是否可搶佔。

3.ReentrantReadWriteLock,用於讀多寫少,且讀不需要互斥的場景,大大提高效能。

示例程式碼:

複製程式碼
1、嘗試獲取一次    
    ReentrantLock lock = new ReentrantLock();  
    if (lock.tryLock()) {  //得到執行,得不到不執行,就一次。  
        try
{ //操作 } finally { lock.unlock(); } } 2、同步執行,類似synchronized(也是使用最多的) ReentrantLock lock = new ReentrantLock(); //引數預設false,不公平鎖:可搶佔 ReentrantLock lock = new ReentrantLock(true); //公平鎖:嚴格按照請求鎖的排隊順序獲取鎖 lock.lock(); //如果被其它資源鎖定,會在此等待鎖釋放,阻塞 try { //操作 } finally { lock.unlock(); } 3、嘗試等待固定時間再次獲取 ReentrantLock lock = new ReentrantLock(true); //公平鎖 try { if (lock.tryLock(5, TimeUnit.SECONDS)) { //如果已經被lock,嘗試等待5s,看是否可以獲得鎖,如果5s後仍然無法獲得鎖則返回false try { //操作 } finally { lock.unlock(); } } } catch (InterruptedException e) { e.printStackTrace(); //當前執行緒被中斷時(interrupt),會拋InterruptedException } 4、可中斷鎖的同步執行 ReentrantLock lock = new ReentrantLock(true); //公平鎖 lock.lockInterruptibly(); try { //操作 } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); }
複製程式碼

三、volatile

volatile,翻譯過來是易變的。只保證同一變數在多執行緒中的可見性。

  1)它確保指令重排序時不會把其後面的指令排到記憶體屏障之前的位置,也不會把前面的指令排到記憶體屏障的後面;即在執行到記憶體屏障這句指令時,在它前面的操作已經全部完成;

  2)它會強制將對快取的修改操作立即寫入主存;

  3)如果是寫操作,它會導致其他CPU中對應的快取行無效。

注意:雖然保證變數是主存資料,但是操作不是原子的,多執行緒讀取到同一個值(是主存的值),同時進行判斷或者操作,導致出錯。

總結:

可見,synchronized和ReentrantLock是一個級別的,但是volatile只是一個輕量級的關鍵字。可用場景:

1.狀態標記

複製程式碼
 1 volatile boolean inited = false;
 2 //執行緒1:
 3 context = loadContext();  
 4 inited = true;            
 5  
 6 //執行緒2:
 7 while(!inited ){
 8 sleep()
 9 }
10 doSomethingwithconfig(context);
複製程式碼

2.double check :使用 volatile 關鍵字來保證多執行緒下的單例

複製程式碼
    public class Singleton {  
        private volatile Singleton instance = null;  
        public Singleton getInstance() {  
            if (instance == null) {  
                synchronized(this) {  
                    if (instance == null) {  
                        instance = new Singleton();  
                    }  
                }  
            }  
            return instance;  
        }  
    }  
複製程式碼

參考資料:

《深入理解Java虛擬機器》

《effective java》

 http://www.cnblogs.com/dolphin0520/p/3920373.html