1. 程式人生 > >Java中的lock介面

Java中的lock介面

一、鎖是用來控制多個執行緒訪問共享資源的方式,一般來說,一個鎖能夠防止多個執行緒同時
訪問共享資源(但是有些鎖可以允許多個執行緒併發的訪問共享資源,比如讀寫鎖)。在Lock接
口出現之前,Java程式是靠synchronized關鍵字實現鎖功能的,而Java SE 5之後,併發包中新增
了Lock介面(以及相關實現類)用來實現鎖功能,它提供了與synchronized關鍵字類似的同步功
能,只是在使用時需要顯式地獲取和釋放鎖。雖然它缺少了(通過synchronized塊或者方法所提
供的)隱式獲取釋放鎖的便捷性,但是卻擁有了鎖獲取與釋放的可操作性、可中斷的獲取鎖以
及超時獲取鎖等多種synchronized關鍵字所不具備的同步特性。

lock特性
特性 描述
非阻塞地獲取鎖 當前執行緒嘗試獲取鎖,如果這時刻沒有被其他執行緒獲取到,則成功獲取鎖
能被中斷地獲取鎖 與synchronize不通過,獲取到鎖地執行緒能夠響應中斷,獲取到鎖地執行緒被中斷時,異常丟擲,釋放鎖,
超時獲取鎖

指定地截至時間之前獲取鎖,若截至時間到了,無法獲取

二、 佇列同步器

1、AbstractQueuedSynchronizer(佇列同步器)是用來構建鎖或者其他同步組
件的基礎框架,它使用了一個int成員變量表示同步狀態,通過內建的FIFO佇列來完成資源獲
取執行緒的排隊工作,併發包的作者(Doug Lea)期望它能夠成為實現大部分同步需求的基礎。

       同步器的主要使用方式是繼承,子類通過繼承同步器並實現它的抽象方法來管理同步狀
態,在抽象方法的實現過程中免不了要對同步狀態進行更改,這時就需要使用同步器提供的3
個方法(getState()、setState(int newState)和compareAndSetState(int expect,int update))來進行操
作,因為它們能夠保證狀態的改變是安全的。子類推薦被定義為自定義同步元件的靜態內部
類,同步器自身沒有實現任何同步介面,它僅僅是定義了若干同步狀態獲取和釋放的方法來
供自定義同步元件使用,同步器既可以支援獨佔式地獲取同步狀態,也可以支援共享式地獲
取同步狀態,這樣就可以方便實現不同型別的同步元件(ReentrantLock、
ReentrantReadWriteLock和CountDownLatch等)。
同步器是實現鎖(也可以是任意同步元件)的關鍵,在鎖的實現中聚合同步器,利用同步
器實現鎖的語義。可以這樣理解二者之間的關係:鎖是面向使用者的,它定義了使用者與鎖交
互的介面(比如可以允許兩個執行緒並行訪問),隱藏了實現細節;同步器面向的是鎖的實現者,
它簡化了鎖的實現方式,遮蔽了同步狀態管理、執行緒的排隊、等待與喚醒等底層操作。鎖和同
步器很好地隔離了使用者和實現者所需關注的領域。

三、同步佇列

同步器依賴內部的同步佇列(一個FIFO雙向佇列)來完成同步狀態的管理,當前執行緒獲取
同步狀態失敗時,同步器會將當前執行緒以及等待狀態等資訊構造成為一個節點(Node)並將其
加入同步佇列,同時會阻塞當前執行緒,當同步狀態釋放時,會把首節點中的執行緒喚醒,使其再
次嘗試獲取同步狀態。
同步佇列中的節點(Node)用來儲存獲取同步狀態失敗的執行緒引用、等待狀態以及前驅和
後繼節點。

同步器包含了兩個節點型別的引用,一個指向頭節點,而另一個指向尾節點。
試想一下,當一個執行緒成功地獲取了同步狀態(或者鎖),其他執行緒將無法獲取到同步狀態,轉
而被構造成為節點並加入到同步佇列中,而這個加入佇列的過程必須要保證執行緒安全,因此
同步器提供了一個基於CAS的設定尾節點的方法:compareAndSetTail(Node expect,Node
update),它需要傳遞當前執行緒“認為”的尾節點和當前節點,只有設定成功後,當前節點才正式
與之前的尾節點建立關聯。

同步佇列遵循FIFO,首節點是獲取同步狀態成功的節點,首節點的執行緒在釋放同步狀態
時,將會喚醒後繼節點,而後繼節點將會在獲取同步狀態成功時將自己設定為首節點。

設定首節點是通過獲取同步狀態成功的執行緒來完成的,由於只有一個執行緒能
夠成功獲取到同步狀態,因此設定頭節點的方法並不需要使用CAS來保證,它只需要將首節
點設定成為原首節點的後繼節點並斷開原首節點的next引用即可。