下圖簡單表示 mutual exclusion 的意義
在 Java 1.4 可使用 synchronized 關鍵字來達到 mutex 的目的,但使用 synchronized 有些功能不易做到,例如
- 如果發現 lock 已被佔用,就只能等待,無法去處理其他的工作
- 在等待獲取鎖的狀況下,無法被 interrupt
- 自訂取得鎖的規則(公平鎖,非公平鎖)
-
ReentrantLock中文翻譯為"重入鎖",就字面上的意義代表當某個 thread 獲取某個鎖後,在未釋放鎖的情況下,可以獲得同一把鎖多次,因為 ReentrantLock 內部存在著一個計數器,計算取得鎖的次數,如果取得 lock 兩次,就必須 unlock 兩次才可真正的釋放鎖,這樣的設計可方便使用遞迴的方式呼叫(recursive call) lock()
基本使用方式
final Lock lock = new ReentrantLock(); lock.lock(); try { // do something ... } finally { lock.unlock(); }
遞迴呼叫
private final ReentrantLock lock = new ReentrantLock(); public void execute() { try { lock.lock();//This lock supports a maximum of 2147483647 recursive locks by the same thread. System.out.println("lock|HoldCount=" + lock.getHoldCount()); if (lock.getHoldCount() < 10) execute();//recursive locking by a single thread } finally { lock.unlock(); System.out.println("unlock|HoldCount=" + lock.getHoldCount() + "|isHeldByCurrentThread=" + lock.isHeldByCurrentThread()); } }
從 Java 的 API Specification 我們可以看到 ReentrantLock 的 constructor 在不傳值的狀況下的狀況下,預設是個非公平鎖,如果我們要讓獲得鎖公平,就必需維持 thread 的排隊順序,這需要額外開銷,對執行效率是不利的,雖然獲取鎖的原則是不公平的,但 JVM 保證所有 thread 最終會獲得它們等待的鎖,避免 thread starvation
ReentrantLock 有 lock, tryLock, lockInterruptibly 幾個獲取 lock 的 method
- lock() : 如果拿不到 lock 就一直等待,直到拿到 lock 為止
- tryLock() : 如果拿到鎖就 return true, 否則就 return false
- tryLock(long timeout, TimeUnit unit) : 如果拿不到 lock, 則等待一段時間
- lockInterruptibly() : 如果拿不到 lock 就一直等待, 在等待的過程中 thread 可以被 interrupted
public void testLock() throws Exception { private final Lock lock = new ReentrantLock(); lock.lock(); Thread t = new Thread(new Runnable() { public void run() { lock.lock(); System.out.println(Thread.currentThread().getName() + " acquires the lock"); } }); t.start(); Thread.sleep(3000); t.interrupt();//無作用,因 thread 並非在 wait,join,sleep 狀態 Thread.sleep(Long.MAX_VALUE); }
public void testLockInterruptibly() throws Exception { private final Lock lock = new ReentrantLock(); lock.lock(); Thread t = new Thread(new Runnable() { public void run() { try { lock.lockInterruptibly(); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() + " interrupted."); } } }); t.start(); Thread.sleep(3000); t.interrupt();//thread 在等待獲得 lock 的過程中被中斷 Thread.sleep(Long.MAX_VALUE); }
- ReentrantReadWriteLock 讀寫鎖內部維護一對鎖, 一把用於讀取的操作, 另一把用於寫入操作, 讀取鎖可同時被多個讀取者擁有, 而此時鎖所保護的資料處於唯讀狀態, 讀寫鎖是互斥的, 因此當沒有任何讀取或寫入鎖定時, 才可以取得寫入鎖定, 但是對於讀取來說沒有互斥性,可參考 Java Doc 對於 Interface ReadWriteLock 的說明 , 另外使用 ReentrantReadWriteLock 時, 在讀取 thread 很多, 寫入 thread 很少的狀況下, 可能會使得寫入 thread 一直處於等待狀態, 因此必需留意 thread starvation 的問題
0 comments:
Post a Comment