+ ReentrantLock類的使用
+ ReentrantReadWriteLock類的使用
1. 使用ReentrantLock類
ReentrantLock類能夠實現線程之間同步互斥,並且在擴展功能上更加強大,例如嗅探技術
、多路分支通知
等功能,在使用上比synchronized
更加靈活。
ReentrantLock類具有完全互斥排他的效果,即同一時間只有一個線程在執行ReentrantLock.lock()
方法后面的任務。
ReentrantLock
實現了Lock
接口,該接口包含以下方法:
public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition(); }
關鍵字 synchronized 與 wait() 和 notify() / notifyAll() 方法結合可以實現等待/通知模型, ReentrantLock 類也可以實現同樣的功能,需要借助 Condition對象。
Condition 對象是JDK1.5中出現的技術,它有更好的靈活性,比如可實現多路通知功能,也就是在一個 Lock 對象里面可以創建多個 Condition (即對象監視器)實例,線程對象可以注冊在指定的 Condition 中,從而可以有選擇性地進行線程通知,在調度線程上更加靈活。
使用 notify() / notifyAll() 方法進行通知時,被通知的線程由JVM隨機選擇。通過 ReentrantLock 結合 Condition 可以實現選擇性通知。
synchronized 相當於整個 Lock 對象中只有一個單一的 Condition 對象,所有的線程都注冊在它一個對象的身上。線程 notifyAll() 時,需要通知所有的 WAITING 線程,沒有選擇權。
Object 類中的 wait() 方法相當於 Condition 類中的 await() 方法。
Object 類中的 wait(long timeout) 方法相當於 Condition 類中的 await(long time,TimeUnit unit) 方法。
Object 類中的 notify() 方法相當於 Condition 類中的 signal() 方法。
Object 類中的 notifyAll() 方法相當於 Condition 類中的 signalAll() 方法。
公平鎖與非公平鎖
Lock 分為公平鎖和非公平鎖,公平鎖表示線程獲取鎖的順序是按照線程加鎖的順序來分配的,即先來先得的 FIFO先進先出順序。非公平鎖是一種獲取鎖的搶占機制,是隨機獲得鎖的。
Lock方法
- 方法 int getHoldCount() 的作用是查詢當前線程保持此鎖定的個數,也就是調用 lock()方法的次數。
- 方法 int getQueueLength() 的作用是返回正在等待獲取此鎖定的線程估計數
- 方法 int getWaitQueueLength(Condition condition) 的作用是返回等待與此鎖定相關的給定條件 Condition 的線程估計數。比如有5個線程,每個線程都執行了同一個 condition 對象的 await() 方法,則調用 getWaitQueueLength(condition) 方法返回的指為5.
- 方法 boolean hasQueuedThread(Thread thread) 的作用是查詢指定的線程是否正在等待獲取此鎖定。
- 方法 boolean hasQueuedThreads() 的作用是差選是否有線程正在等待獲取此鎖定。
- 方法 boolean hasWaiters(Condition condition) 的作用是查詢是否有線程正在等待與此鎖定有關的 condition 條件。
- 方法 boolean isFair() 的作用是判斷是不是公平鎖。
- 方法 boolean isHeldByCurrentThread() 的作用是查詢當前線程是否保持此鎖定。
- 方法 boolean isLocked() 的作用是查詢此鎖定是否由任意線程保持。
- 方法 void lockInterruptibly() 的作用是:如果當前線程未被中斷,則獲取鎖定;如果已經被中斷則拋出異常( java.lang.InterruptedException ).
- 方法 boolean tryLock() 的作用是:僅在調用時鎖未被另外線程保持的情況下,才獲取此鎖定。
- 方法 boolean tryLock(long timeout,TimeUnit unit) 的作用是:如果鎖定在給定等待時間內沒有被另一個線程保持,且當前線程未被中斷,則獲取該鎖定。
使用Condition對象可以對線程執行的業務進行排序規划
2. 使用ReentrantReadWriteLock類
讀寫鎖有兩個鎖:一個是讀操作相關的鎖,也稱為共享鎖;一個是寫操作相關的鎖,也稱為排他鎖。
多個讀鎖之間不互斥;讀鎖與寫鎖互斥;寫鎖與寫鎖互斥。
package com.zxd.concurrent.learning; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * @author CoderZZ * @Title: ${FILE_NAME} * @Project: ConcurrentLearning * @Package com.zxd.concurrent.learning * @description: 讀鎖不互斥;讀寫、寫寫互斥 * @Version 1.0 * @create 2018-04-08 21:29 **/
public class ReentrantReadWriteLockTest { private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock(); public void read(){ try { reentrantReadWriteLock.readLock().lock(); System.out.println("獲得讀鎖"+Thread.currentThread().getName()+" timestamp:"+System.currentTimeMillis()); Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); }finally { reentrantReadWriteLock.readLock().unlock(); } } public void write(){ try{ reentrantReadWriteLock.writeLock().lock(); System.out.println("獲得寫鎖"+Thread.currentThread().getName()+" timestamp:"+System.currentTimeMillis()); Thread.sleep(10000); }catch (Exception e){ e.printStackTrace(); }finally { reentrantReadWriteLock.writeLock().unlock(); } } public static void main(String[] args){ ReentrantReadWriteLockTest reentrantReadWriteLockTest = new ReentrantReadWriteLockTest(); Runnable readRunnable = new Runnable() { @Override public void run() { reentrantReadWriteLockTest.read(); } }; Thread threadA = new Thread(readRunnable); threadA.setName("A"); threadA.start(); Thread threadB = new Thread(readRunnable); threadB.setName("B"); threadB.start(); Runnable writeRunnable = new Runnable() { @Override public void run() { reentrantReadWriteLockTest.write(); } }; Thread writeA = new Thread(writeRunnable); writeA.setName("writeA"); writeA.start(); Thread writeB = new Thread(writeRunnable); writeB.setName("writeB"); writeB.start(); } }
運行結果如下: