高級java必會系列二:多線程經常使用的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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM