Lock

lock的兩個子類——ReentrantLock和ReentrantReadWriteLock

ReentrantLock

相比synchronized功能更加強大

用法

調用ReentrantLock的lock方法加鎖,unlock方法解鎖(一般在finally中寫unlock)

Object中的wait()、wait(long)等價于Condition中的await()、await(long time, TimeUnit unit)

Object中的notify()、notifyAll()等價于Condition中的singal()、signalAll()

使用Condition實現等待/通知

ReentrantLock可以實現如果關鍵字synchronized與wait、notify等的功能,但需要借助Condition對象,使用Condition可以有更好的靈活性——比如多路通知,即可以在一個Lock對象里面創建多個Condition(即對象監視器)實例,,編程對象可以注冊在指定的Condition中,從而可以有選擇性地進行線程通知,在調度線程上更有靈活性。

使用notify/notifyAll,被通知的線程是JVM選擇的,而ReentrantLock結合Condition類可以實現“選擇性通知”,事實上,synchronized相當于整個Lock對象中只有一個單一的Condition對象,所有的線程都注冊在它一個對象身上。線程開始notifyAll的時候,需要通知所有的waiting線程,產生效率問題。

實現通知部分線程:

private Lock lock = new ReentrantLock();
private Condition cA = lock.newCondition();
private Condition cB = lock.newCondition();
...

在某wait函數中:
lock.lock();
conditionA.await();

在某notify函數中:
lock.lock();
conditionA.signalAll();

公平鎖與非公平鎖

公平鎖:線程獲取鎖的順序是按照線程加鎖的順序來分配的,類似FIFO,但只是大部分線程按照FIFO

非公平鎖:未必按照FIFO,可能導致部分線程永遠獲取不到鎖

Lock lock = new ReentrantLock(isFair)

通過設置如上的isFair判斷是否為公平鎖(默認情況下,ReentrantLock為非公平鎖);當持有鎖的時間或者請求鎖的時間較長的時候,使用公平鎖好一些,因為非公平鎖的插隊帶來的效率提升此時可能不會有

非公平鎖的性能優于公平鎖,原因如下:

  • 從線程進入了RUNNABLE狀態到run狀態是要比較久的時間的。而且,在一個鎖釋放之后,其他的線程會需要重新來獲取鎖。其中經歷了持有鎖的線程釋放鎖,其他線程從掛起恢復到RUNNABLE狀態,其他線程請求鎖,獲得鎖,線程執行,這一系列步驟。如果這個時候,存在一個線程直接請求鎖,可能就避開掛起到恢復RUNNABLE狀態的這段消耗,所以性能更優化。 相當于等待一個阻塞的進程和一個就緒的進程進入運行使用CPU的狀態
  • 公平鎖需要多加入一個條件判斷

getHoldCount()、getQueueLength()、getWaitQueueLength()的測試

  • getHoldCount()

    查詢當前線程保持此鎖定的次數,也就是調用lock方法的次數

  • getQueueLength()

    返回正等待獲取此鎖定的線程估計數,比如有5個線程,1個線程首先執行await()方法,調用此函數的返回值是4,說明有4個線程同時在等待lock的釋放

  • getWaitQueueLength(Condition)

    回等待與此鎖相關的給定條件Condition的線程估計數,比如5個線程,每個賢臣都執行了同一個Condition的await方法,則調用此函數的返回值為5

hasQueuedThread、hasQueuedThreads、hasWaiters的測試

  • hasQueuedThread(Thread)

    查詢指定的線程是否在等待獲取此鎖定

  • hasQueuedThreads()

    查詢是否有線程在等待獲取此鎖定

  • hasWaiters(Condition)

    查詢是否有線程在等待與此鎖相關的condition條件

isFair()、isHeldByCurrentThread()、isLocked的測試

  • isFair()

    判斷是否為公平鎖

  • isHeldByCurrentThread()

    查詢當前線程是否保持此鎖定,一般用于unlock前的條件判斷

  • isLocked()

    判斷此鎖是否由任意線程持有

lockInterruptibly()、tryLock()、tryLock(long timeout, TimeUnit unit)的測試

  • lockInterruptibly()

    如果當前線程未被中斷,則獲取鎖定;否則拋出異常(可以利用此終止異常)

  • tryLock()

    調用時鎖定未被另一個線程持有的情況下,才獲取此鎖定

  • tryLock(long timeout, TimeUnit unit)

    如果鎖定在給定等待時間內沒有被另一個線程保持,且當前線程未被中斷,則獲取該鎖定。

ReentrantReadWriteLock類

類ReentrantLock具有完全互斥排他的效果,即同一時間只有一個線程在執行ReentrantLock.lock方法后面的任務,雖然安全,但效率低。

而ReentrantReadWriteLock類可以在某些不需要操作實例變量的方法中,使用讀寫鎖來提高方法的代碼運行速度。

讀寫鎖有兩個鎖:讀——共享鎖,寫——排它鎖。

使用

ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

lock.readLock.lock()

則可以有多個線程共享讀

如果把readLock改成writeLock,則將會導致讀寫、寫寫互斥

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容