目錄java
ReentrantLock 是Java併發包中提供的一個可重入的互斥鎖。ReentrantLock 和 synchronized 在基本用法和行爲語義上基本相同,一樣具備可重入性。只是 ReentrantLock 增長了一些高級擴展功能,好比,能夠實現公平鎖,同時也能夠實現綁定多個 Condition。併發
可重入性: 一個線程在獲取一段使用鎖的代碼時,能夠再次進入這段代碼。例如,一個被可重入鎖修飾的遞歸程序,能夠重複的獲取鎖,而不會出現把本身鎖死的狀況。synchronized 和 ReentrantLock 都具備可重入性。ide
公平鎖 / 非公平鎖
所謂公平鎖,指鎖的獲取策略相對公平,當多個線程在獲取同一個鎖時,必須按照鎖的申請時間來依次得到鎖;非公平鎖則不一樣。synchronized 是非公平鎖,ReentrantLock 默認也是非公平的,可是能夠經過帶 boolean 參數的構造方法指定使用公平鎖,但非公平鎖的性能通常要優於公平鎖。
synchronized 是 Java 原生的互斥同步鎖,使用方便,對於 synchronized 修飾的方法或同步塊,無需再顯式釋放鎖。而 ReentrantLock 作爲 API 層面的互斥鎖,須要顯式地去加鎖解鎖。採用 Lock ,必須主動去釋放鎖,而且在發生異常時,不會自動釋放鎖。所以通常來講,使用 Lock 必須在 try{}catch{} 塊中進行,而且將釋放鎖的操做放在 finally 塊中進行,以保證鎖必定被被釋放,防止死鎖的發生。函數
class Demo{ private final ReentrantLock lock = new ReentrantLock(); public void method(){ lock.lock(); // 加鎖 try{ // to do... }finally { lock.unlock(); // 釋放鎖 } } }
/** * Creates an instance of {@code ReentrantLock}. * This is equivalent to using {@code ReentrantLock(false)}. */ public ReentrantLock() { sync = new NonfairSync(); }
/** * Creates an instance of {@code ReentrantLock} with the * given fairness policy. * * @param fair {@code true} if this lock should use a fair ordering policy */ public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
public void lock() { sync.acquire(1); //代理到Sync的lock方法上 }
Sync的lock方法是抽象的,實際的lock會代理到FairSync或是NonFairSync上(根據用戶的選擇來決定,公平鎖仍是非公平鎖)oop
public void unlock() { sync.release(1); }
釋放鎖,調用sync的release方法。源碼分析
public void method(){ if(lock.tryLock()){ try{ // to do... }finally { lock.unlock(); } }else { // 若是不能獲取鎖,則作其餘事情 } }
tryLock()方法是有返回值的,它表示用來嘗試獲取鎖,若是獲取成功,則返回true,若是獲取失敗(即鎖已被其餘線程獲取),則返回false。性能
public Condition newCondition() { return sync.newCondition(); }
獲取一個conditon,ReentrantLock支持多個Conditionui
/* * 編寫一個程序,開啓 3 個線程,這三個線程的 ID 分別爲 A、B、C,每一個線程將本身的 ID 在屏幕上打印 10 遍,要求輸出的結果必須按順序顯示。 * 如:ABCABCABC…… 依次遞歸 */ class AlternateDemo{ private int num = 1; // 標記當前正在執行的線程 private ReentrantLock lock = new ReentrantLock(); private Condition condition1 = lock.newCondition(); private Condition condition2 = lock.newCondition(); private Condition condition3 = lock.newCondition(); /** * * @param totalLoops 循環第幾輪 */ public void loopA(int totalLoops){ lock.lock(); try{ if(num != 1){ condition1.await(); } System.out.println(Thread.currentThread().getName() + "\t" + totalLoops); num = 2; condition2.signal(); }catch (Exception e){ }finally { lock.unlock(); } } public void loopB(int totalLoops){ lock.lock(); try{ if(num != 2){ condition2.await(); } System.out.println(Thread.currentThread().getName() + "\t" + totalLoops); num = 3; condition3.signal(); }catch (Exception e){ }finally { lock.unlock(); } } public void loopC(int totalLoops){ lock.lock(); try{ if(num != 3){ condition3.await(); } System.out.println(Thread.currentThread().getName() + "\t" + totalLoops); num = 1; condition1.signal(); }catch (Exception e){ }finally { lock.unlock(); } } } public class Main { public static void main(String[] args) { AlternateDemo alternateDemo = new AlternateDemo(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { alternateDemo.loopA(i); } } }, "A").start(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { alternateDemo.loopB(i); } } }, "B").start(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { alternateDemo.loopC(i); System.out.println("----------------------"); } } }, "C").start(); } }
代碼分析:三個線程A、B、C分別調用10次打印,只需想辦法控制三個現成的執行順序。
若線程A先獲取鎖,直接打印。
若線程B先獲取鎖,B會被阻塞,釋放鎖後由A、C爭奪。
若線程C先獲取鎖,C會被阻塞,釋放鎖後由A、B爭奪。
使用一個變量來維持獲取鎖的順序,分別是線程A、線程B、線程Cthis