天天看點

簡單對比一下synchronized、volatile、ReentrantLock

作者:索碼理

聊之前先說一下并發程式設計的3個特性。

并發程式設計特性

  1. 原子性:對共享資源的一組操作,要麼成功要麼失敗,不會出現部分成功部分失敗的情況。
  2. 可見性: 當線程擷取到瑣時,會拷貝一份共享資源到本地記憶體,釋放鎖時會将共享資源重新整理到主記憶體中。可見性是指當共享資源發生變化時,其他線程都能夠看到這個變化。
  3. 有序性:為了提高效率,編譯器和處理器會對代碼進行指令重排,單線程的情況下,指令重拍不會受到影響,多線程情況下可能會影響代碼執行的正确性。有序性是指代碼編寫順序和執行順序是一緻的。

volatile

volatile 是JVM提供的最輕量級的同步機制,編譯器不會對其進行優化。

  • 特性:

volatile 隻保證共享資源的可見性和有序性。

使用volatile修飾共享資源時,如果共享資源變化時,會直接将緩存中的資料寫回到主記憶體中去,資料也是從主記憶體中讀取,進而保證了可見性。

volatile 底層是通過作業系統的記憶體屏障來實作的,由于使⽤了記憶體屏障,是以會禁⽌指令重排,進而就保證了有序性。

  • 作用的地方:

volatile 隻能用來修飾成員變量。

public class VolatileTest {
    
    private volatile static String staticVolatile;
    
    private volatile String memberVolatile;
}
           

synchronized

synchronized關鍵字是java提供的内置鎖來保證我們對共享資源的同步,它會自動加鎖和釋放鎖,它的鎖是非公平鎖, synchronized關鍵字标記的地方會被編譯器進行優化。synchronized會使線程串行執行,可能會造成線程阻塞。

  • 特性

synchronized關鍵字使線程串行化執行,是以保證了并發安全的3個特性,并且還擁有以下兩個特性:

  • 互斥性:同時隻有一個線程能夠通路synchronized方法或者同步代碼塊。
  • 可重入性:synchronized是可重入鎖,通俗解釋可重入鎖就是當一個線程擷取到了某個對象鎖或者類鎖之後,這個線程在未釋放鎖之前,再調用該鎖的其他synchronized方法或代碼塊時,不用再次重新獲得鎖。
  • 作用的地方

synchronized關鍵字可用來修飾方法或者代碼塊。

  1. 修飾方法,分為執行個體方法和靜态方法
  • 修飾執行個體方法,對象鎖
public  synchronized void objectMethods(){
   .....
}           
  • 修飾靜态方法,類鎖
public static  synchronized void staticMethods(){
   .....
 }           
  1. 修飾代碼塊
  • obj為對象的引用 對象鎖
public  void objectMethods(){
 synchronized (obj){
 }
}
           
  • Object 為某個類 類鎖
public  void classLock(){
 synchronized (Object.class){
 }
}           

ReentrantLock

Lock 是 Java 5提供的一個具有鎖機制的接口,ReentrantLock 是Lock的一個實作,内部是通過AQS(AbstractQueuedSynchronizer)實作的。ReentrantLock翻譯過來是可重入鎖,它和synchronized類似,ReentrantLock 需要手動加鎖和釋放鎖, 相對于synchronized它更加靈活,提供了更多的方法。 ReentrantLock 有公平鎖和非公平鎖兩種方式,預設是使用公平鎖。

  • 特性

ReentrantLock 是可重入的同步鎖,是以它除了具有并發程式設計的三大特性,還具有可重入性。

  • 作用的地方

ReentrantLock 是一個類,它既可以作為成員變量,也可以作為局部變量使用。做為成員變量和局部變量時,使用的方式有一點點不同,不管使用哪種方式,最後都别忘了要調用unlock() 方法手動釋放鎖。

  1. 做為成員變量使用格式:
private Lock globalLock = new ReentrantLock();

public void globalLock(){
    if (globalLock.tryLock()) {
        try {
        } catch (Exception e) {
          
        }finally {
            globalLock.unlock();
        }
    }
}
           

上面的tryLock() 方法是嘗試擷取鎖,如果擷取成功傳回true,否則傳回false,也可以換成加了等待時間的 boolean tryLock(long time, TimeUnit unit) 方法,在設定的等待時間内擷取鎖成功則傳回true,否則false。

  1. 做為局部變量使用格式:
public void lock(){
     Lock lock = new ReentrantLock();
     lock.lock();
     try {

     }finally {
         lock.unlock();
     }
 }
           

對比

簡單對比一下三者之間的差別:

volatile synchronized ReentrantLock
是否是關鍵字
是否需要手動加鎖/釋放鎖
是否能保證并發安全
是否是公平鎖 \ 有公平鎖和非公平鎖兩種實作
是否會阻塞線程
JVM是否會對其優化
特性 可見性、有序性 可見性、有序性、有序性 可見性、有序性、有序性
使用的地方 成員變量 方法、代碼塊 成員變量、局部變量

繼續閱讀