1. 程式人生 > >Synchronized與ReentrantLock區別總結(簡單粗暴,一目瞭然)

Synchronized與ReentrantLock區別總結(簡單粗暴,一目瞭然)

這篇文章是關於這兩個同步鎖的簡單總結比較,關於底層原始碼實現原理沒有過多涉及,後面會有關於這兩個同步鎖的底層原理篇幅去介紹。

相似點:

這兩種同步方式有很多相似之處,它們都是加鎖方式同步,而且都是阻塞式的同步,也就是說當如果一個執行緒獲得了物件鎖,進入了同步塊,其他訪問該同步塊的執行緒都必須阻塞在同步塊外面等待,而進行執行緒阻塞和喚醒的代價是比較高的(作業系統需要在使用者態與核心態之間來回切換,代價很高,不過可以通過對鎖優化進行改善)。

功能區別:

這兩種方式最大區別就是對於Synchronized來說,它是java語言的關鍵字,是原生語法層面的互斥,需要jvm實現。而ReentrantLock它是JDK 1.5之後提供的API層面的互斥鎖,需要lock()和unlock()方法配合try/finally語句塊來完成

便利性:很明顯Synchronized的使用比較方便簡潔,並且由編譯器去保證鎖的加鎖和釋放,而ReenTrantLock需要手工宣告來加鎖和釋放鎖,為了避免忘記手工釋放鎖造成死鎖,所以最好在finally中宣告釋放鎖。

鎖的細粒度和靈活度:很明顯ReenTrantLock優於Synchronized

效能的區別:

在Synchronized優化以前,synchronized的效能是比ReenTrantLock差很多的,但是自從Synchronized引入了偏向鎖,輕量級鎖(自旋鎖)後,兩者的效能就差不多了,在兩種方法都可用的情況下,官方甚至建議使用synchronized,其實synchronized的優化我感覺就借鑑了ReenTrantLock中的CAS技術。都是試圖在使用者態就把加鎖問題解決,避免進入核心態的執行緒阻塞。

1.Synchronized

Synchronized進過編譯,會在同步塊的前後分別形成monitorenter和monitorexit這個兩個位元組碼指令。在執行monitorenter指令時,首先要嘗試獲取物件鎖。如果這個物件沒被鎖定,或者當前執行緒已經擁有了那個物件鎖,把鎖的計算器加1,相應的,在執行monitorexit指令時會將鎖計算器就減1,當計算器為0時,鎖就被釋放了。如果獲取物件鎖失敗,那當前執行緒就要阻塞,直到物件鎖被另一個執行緒釋放為止。

public class SynDemo{
 
	public static void main(String[] arg){
		Runnable t1=new MyThread();
		new Thread(t1,"t1").start();
		new Thread(t1,"t2").start();
	}
 
}
class MyThread implements Runnable {
 
	@Override
	public void run() {
		synchronized (this) {
			for(int i=0;i<10;i++)
				System.out.println(Thread.currentThread().getName()+":"+i);
		}
		
	}
 
}


2.ReentrantLock

由於ReentrantLock是java.util.concurrent包下提供的一套互斥鎖,相比Synchronized,ReentrantLock類提供了一些高階功能,主要有以下3項:

        1.等待可中斷,持有鎖的執行緒長期不釋放的時候,正在等待的執行緒可以選擇放棄等待,這相當於Synchronized來說可以避免出現死鎖的情況。通過lock.lockInterruptibly()來實現這個機制。

        2.公平鎖,多個執行緒等待同一個鎖時,必須按照申請鎖的時間順序獲得鎖,Synchronized鎖非公平鎖,ReentrantLock預設的建構函式是建立的非公平鎖,可以通過引數true設為公平鎖,但公平鎖表現的效能不是很好。

公平鎖、非公平鎖的建立方式:

//建立一個非公平鎖,預設是非公平鎖
Lock lock = new ReentrantLock();
Lock lock = new ReentrantLock(false);

//建立一個公平鎖,構造傳參true
Lock lock = new ReentrantLock(true);

        3.鎖繫結多個條件,一個ReentrantLock物件可以同時繫結對個物件。ReenTrantLock提供了一個Condition(條件)類,用來實現分組喚醒需要喚醒的執行緒們,而不是像synchronized要麼隨機喚醒一個執行緒要麼喚醒全部執行緒

ReenTrantLock實現的原理:

之後還會總結一篇ReenTrantLock相關的原理底層原理分析,簡單來說,ReenTrantLock的實現是一種自旋鎖,通過迴圈呼叫CAS操作來實現加鎖。它的效能比較好也是因為避免了使執行緒進入核心態的阻塞狀態。想盡辦法避免執行緒進入核心的阻塞狀態是我們去分析和理解鎖設計的關鍵鑰匙。

什麼情況下使用ReenTrantLock:

答案是,如果你需要實現ReenTrantLock的三個獨有功能時。

ReentrantLock的用法如下:

public class SynDemo{
 
	public static void main(String[] arg){
		Runnable t1=new MyThread();
		new Thread(t1,"t1").start();
		new Thread(t1,"t2").start();
	}
 
}
class MyThread implements Runnable {
 
	private Lock lock=new ReentrantLock();
	public void run() {
			lock.lock();
			try{
				for(int i=0;i<5;i++)
					System.out.println(Thread.currentThread().getName()+":"+i);
			}finally{
				lock.unlock();
			}
	}
 
}

對ReentrantLock的原始碼分析這有一篇很好的文章

後續在補充的問題:

Synchronized的原理

ReentrantLock的原理。

ReentrantLock為什麼是可重入的。

公平鎖和非公平鎖是什麼?有什麼區別。