Lock鎖接口在JAVA SE5之后,出現(xiàn)在并發(fā)包中.它提供了與synchronized關(guān)鍵字一樣的同步功能.只是在使用時需要顯式地獲取和釋放鎖,缺點就是缺少像synchronized那樣隱式獲取釋放鎖的便捷性,但是卻擁有了鎖獲取與釋放的可操作性,可中斷的獲取鎖以及超時獲取鎖等多種synchronized關(guān)鍵字所不具備的同步特性。
可重入鎖和不可重入鎖
- 可重入鎖:一個線程調(diào)用一個加鎖的方法后,還可以調(diào)用其他加同一把鎖的方法.
- 不可重入鎖:一個線程獲取到一把鎖之后,該線程是無法調(diào)用其他加了該鎖的方法.這種情況,容易造成死鎖.比如:方法一添加了不可重入鎖,方法二添加了不可重入鎖,并且調(diào)用了方法一.這種情況下,調(diào)用方法二就會出現(xiàn)死鎖的情況.那么.重入鎖就可以避免這種情況.
- 常用的可重入鎖:
Sychronized,java.util.concurrent.locks.ReentrantLock
ReentrantLock基本使用
ReentrantLock對資源進行加鎖,同一時刻只會有一個線程能夠占有鎖.當前鎖被線程占有時,其他線程會進入掛起狀態(tài),直到該鎖被釋放,其他掛起的線程會被喚醒并開始新的競爭.
public class ReentrantLockExample {
private ReentrantLock reentrantLock = new ReentrantLock();
private void test(){
reentrantLock.lock();
System.out.println("進行原子操作");
try {
Thread.sleep(3000l);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
ReentrantLockExample reentrantLockExample = new ReentrantLockExample();
for (int i = 0; i < 5; i++) {
executorService.submit(new Thread(reentrantLockExample::test));
}
}
}
通過控制臺的輸出信息可知:每隔三秒輸出一次信息.
ReentrantLock很大程度的依賴了抽象類AbstractQueuedSynchronizer,這里需要先對AbstractQueuedSynchronizer進行了解.看我下面的這篇文章
ReentrantLock又有公平鎖和非公平鎖之分,所以可以看到在源碼中有兩個鎖的實現(xiàn)

公平鎖:每個線程的資源競爭是公平的.按照自身線程調(diào)用lock方法的順序來獲取鎖,即先到先得.
非公平鎖:每個線程的資源競爭是順序不定,誰的優(yōu)先級高,那么誰就會先獲得鎖.
ReentrantLock幾個重要方法的剖析
- 構(gòu)造方法:ReentrantLock的無參構(gòu)造器,默認將局部變量sync賦值為NonfairSycn.
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
- lock():調(diào)用sycn內(nèi)部類的lock方法.
1.獲取一把鎖,如果該鎖沒有被其他線程持有,則立即返回,并將鎖計數(shù)器加一;
2.如果當前線程已經(jīng)持有該鎖,則將鎖計數(shù)器加一,并立即返回.
3.如果該鎖被其他線程持有,那么當前線程的目的將會無效并進入休眠狀態(tài).直到鎖計數(shù)器的值為1.
public void lock() {
sync.lock();
}
這里先看一下,公平鎖的lock方法的實現(xiàn).
FairSync:默認調(diào)用AbstractQueuedSynchronizer的acquire()方法.
final void lock() {
acquire(1);
}
AbstractQueuedSynchronizer:
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
非公平鎖的lock方法實現(xiàn):
NonfairSync:
final void lock() {
//通過CAS操作,將AQS中的stateOffset從0改為1.
if (compareAndSetState(0, 1))
//將當前線程設(shè)為獨享線程
setExclusiveOwnerThread(Thread.currentThread());
else
//否則,再次請求同步狀態(tài).一般參數(shù)為0是釋放鎖,參數(shù)為1是獲取鎖
acquire(1);
}
AbstractQueuedSynchronizer:其中tryAcquire()是由具體的實現(xiàn)類實現(xiàn)的.
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
這里分為兩步進行分析:
1.NonfairSync:執(zhí)行tryAcquire方法
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
//獲取當前AQS的狀態(tài)
int c = getState();
if (c == 0) {//同步狀態(tài)為0時,執(zhí)行CAS操作
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}//當前線程已經(jīng)獲取到鎖,因為當前是重入鎖,則state+1,并返回true
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
2.NonfairSync:tryAcquire方法返回false之后,會執(zhí)行acquireQueued方法.
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
//尾節(jié)點不為空
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
因為NonfairSync是非公平鎖,所以對于新來的線程和同步隊列的線程,都能調(diào)用這個方法來獲取到鎖.
由于實現(xiàn)過于復(fù)雜,這里總結(jié)來說:當多個線程賴競爭鎖時,只會有一個線程能夠獲取到鎖,那么其他線程將會通過CAS操作(保證數(shù)據(jù)的一致性)來將當前線程添加到同步隊列中,當所有線程進入到同步隊列之后,就會進入自旋狀態(tài)(死循環(huán)判斷當前線程所在節(jié)點的前驅(qū)節(jié)點是否為head節(jié)點)去嘗試獲取同步狀態(tài).
- lockInterruptibly()或者tryLock()
可中斷的獲取方式.兩者最終都會調(diào)用方法:
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
如果檢測到線程的中斷,將會直接拋出異常.