多線程學習筆記二之JUC組件

概述

  爲了對共享資源提供更細粒度的同步控制,JDK5新增了java.util.concurrent(JUC)併發工具包,併發包新增了Lock接口(以及相關實現類)用來實現鎖功能,它提供了與synchronized關鍵字相似的同步功能,只是在使用時需要顯式地獲取和釋放鎖,還具備內置鎖不具備的自由操作鎖獲取和釋放、可中斷地獲取鎖以及超時獲取鎖等多種synchronized關鍵字不具備的同步特性。
  爲了實現JUC提出的各種功能的鎖,JUC包的作者,併發大師Doug Lee提出了同步器 synchronizer的概念,在同步器中定義了共享資源的同步狀態,維護了一個雙端的先入先出的同步隊列用於存放獲取共享資源失敗而等待的線程,線程利用同步器實現的鎖獲取共享資源流程如下:

爲了實現上述操作,需要下面三個基本組件的相互協作:

  • 對共享資源同步狀態進行原子性管理 ---> 利用CAS對同步狀態進行更新

  • 線程的阻塞與喚醒 ---> 調用native方法

  • 等待隊列的管理 ---> 維護FIFO隊列

  由此可以看出,同步器是實現鎖的關鍵,同步器面向的是線程訪問和資源控制,它定義了線程對資源是否能夠獲取以及線程的排隊等操作。關於同步器的詳細解釋會在AQS(AbstractQueuedSynchronizer)解析裏給出。

JUC鎖框架圖

  JUC中Lock接口定義了鎖的規範,各種功能的鎖都實現了Lock接口,各個鎖以內部類繼承AQS同步器的方式聚合了同步器,從而以同步器爲基石實現具體功能的鎖。

  1. Lock
      Lock接口爲獨佔鎖(同一時間共享資源只能由一個線程獲取),共享鎖(同一時間共享資源可由多個線程獲取),公平鎖(各個線程獲得鎖的機會是公平的),非公平鎖(各個線程獲得鎖的機會是公平的),重入鎖(線程在獲取到鎖之後,再次獲取該鎖而不會被該鎖所阻塞,不會自己把自己鎖在外面)提供了實現規範,接口中定義的方法如下:

  2. AbstractQueuedSynchronizer
      AbstractQueuedSynchronizer就是被稱之爲AQS的類,可以用於構建鎖或者其他相關同步裝置的基礎框架。從圖中也可以看出,ReentrantLock,ReentrantReadWriteLock,CountDownLatch,CyclicBarrier和Semaphore這些類通過內部類繼承AQS的方式來實現鎖的功能。

  3. Condition
      Condition需要和Lock聯合使用,它的作用是代替Object監視器方法,可以通過await(),signal()來休眠/喚醒線程。Condition 接口描述了可能會與鎖有關聯的條件變量。這些變量在用法上與使用 Object.wait 訪問的隱式監視器類似,但提供了更強大的功能,接口中定義的方法如下:

  4. LockSurport
      LockSupport中的park() 和 unpark()調用native方法將線程休眠。

  5. ReentrantLock
      ReentrantLock對與共享資源採取的是較爲保守的獨佔策略,即只有一個線程能夠獲得鎖;ReentrantLock支持公平鎖和非公平鎖,默認是非公平鎖;從名稱也能看出,ReentrantLock是可重入鎖。

  6. ReentrantReadWriteLock
      ReentrantReadWriteLock是讀寫鎖接口ReadWriteLock的實現類,它包括子類ReadLock和WriteLock。ReentrantLock是共享鎖,而WriteLock是獨佔鎖。

  7. CountDownLatch
      CountDownLatch是一個同步輔助類,在完成一組正在其他線程中執行的操作之前,它允許一個或多個線程一直等待。

  8. CyclicBarrier
      CyclicBarrier是一個同步輔助類,允許一組線程互相等待,直到到達某個公共屏障點(common barrier point)。因爲該barrier在釋放等待線程後可以重用,所以稱它爲循環的barrier。喜歡的朋友可以進圈:609164807 一起交流 一起進步

  9. Semaphore
      Semaphore是一個計數信號量,它的本質是一個"共享鎖"。信號量維護了一個信號量許可集。線程可以通過調用acquire()來獲取信號量的許可;當信號量中有可用的許可時,線程能獲取該許可;否則線程必須等待,直到有可用的許可爲止。 線程可以通過release()來釋放它所持有的信號量許可。

使用內置鎖還是JUC顯示鎖?

  JUC中的顯示鎖提供了與synchronized內置鎖相同的互斥性與內存可見性,那麼我們的多線程代碼到底使用哪一種鎖來實現同步呢?首先從性能角度考慮,在JDK5 顯示鎖剛推出時,性能是大幅領先於內置鎖的,在隨後的JDK版本中,JVM對內置鎖進行了性能優化,現在二者的性能已經沒有明顯優劣之分;從功能使用上,內置鎖的使用較爲簡單,無需手動獲得以及釋放鎖,而顯示鎖的功能更爲強大,具有更高的靈活性,當我們需要使用到鎖的高級功能,如以響應中斷/支持超時的方式獲取鎖或者自定義實現鎖,這時候可以考慮內置鎖。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章