背景:
什麼是 “可重入”?可重入就是說某個執行緒已經獲得某個鎖,可以再次獲取鎖而不會出現死鎖。即可重入鎖的作用就是為了避免死鎖,java中synchronized和ReentrantLock都是可重入鎖。
//synchronized 可重入鎖 private void test() { // 第一次獲得鎖 synchronized (this) { while (true) { // 第二次獲得同樣的鎖 synchronized (this) { System.out.println("ReentrantLock!"); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } //ReentrantLock可重入鎖 Lock lock = new ReentrantLock(); private void test1() { lock.lock(); try { test2(); } finally { lock.unlock(); } } private void test2() { lock.lock(); try { System.out.println("ReentrantLock!"); } finally { lock.unlock(); } }
一.自定義不可重入鎖
所謂不可重入鎖,即若當前執行緒執行某個方法已經獲取了該鎖,那麼在方法中嘗試再次獲取鎖時,就會獲取不到被阻塞。下面的執行緒執行test1()方法首先獲取lock,接下來執行test2()方法就無法執行test2()中的邏輯,必須先釋放鎖。
// 不可重入鎖 class Lock { //是否佔用 private boolean isLocked = false; //使用鎖 public synchronized void lock() { while (isLocked) {//已經佔用 try { this.wait();//等待 } catch (InterruptedException e) { e.printStackTrace(); } } isLocked = true;//修改標識為已經佔用 } //釋放鎖 public synchronized void unLock() { isLocked = false;//修改標識為未佔用 notify();//喚醒等待執行緒 } } ============使用=========== Lock lock = new Lock(); public void test1() { lock.lock(); test2(); lock.unLock(); } private void test2() { lock.lock(); //... lock.unLock(); }
二.自定義可重入鎖
流程:
- 定義鎖佔用標識、儲存執行緒、執行緒鎖持有數量;
- 使用鎖:判斷是否已經佔用和當前執行緒是否不等於儲存執行緒,如果條件符合進入等待,不符合則修改佔用標識、儲存執行緒為當前執行緒、執行緒鎖數量+1;
- 釋放鎖:判斷當前執行緒是否等於儲存執行緒,條件符合則執行緒鎖數量-1,當執行緒鎖數量=0時,修改佔用標識,喚醒等待執行緒,將儲存執行緒置為null;
// 可重入鎖 class ReLock{ //是否佔用 private boolean isLocked = false; private Thread lockedBy = null; //儲存執行緒 private int holdCount = 0; //使用鎖 public synchronized void lock() throws InterruptedException { Thread t = Thread.currentThread(); while(isLocked && lockedBy != t) { wait(); } isLocked = true; lockedBy = t; holdCount ++; } //釋放鎖 public synchronized void unlock() { if(Thread.currentThread() == lockedBy) { holdCount --; if(holdCount ==0) { isLocked = false; notify(); lockedBy = null; } } } public int getHoldCount() { return holdCount; } } ==============使用=============== public class LockTest { ReLock lock = new ReLock(); public void test1() throws InterruptedException { lock.lock(); System.out.println(lock.getHoldCount()); test2(); lock.unlock(); System.out.println(lock.getHoldCount()); } //可重入 public void test2() throws InterruptedException { lock.lock(); System.out.println(lock.getHoldCount()); //................... lock.unlock(); System.out.println(lock.getHoldCount()); } public static void main(String[] args) throws InterruptedException { LockTest lockTest= new LockTest(); lockTest.test1(); Thread.sleep(1000); System.out.println(lockTest.lock.getHoldCount()); } }