文章轉自於技術人成長之路:https://mp.weixin.qq.com/s/Xl5X30dbIYKE-1lB9NuXAw
Concurrent包的結構層次
在針對並發編程中,Doug Lea大師為我們提供了大量實用,高性能的工具類,針對這些代碼進行研究會讓我們對並發編程的掌握更加透徹也會大大提升我們對並發編程技術的熱愛。這些代碼在java.util.concurrent包下。如下圖,即為concurrent包的目錄結構圖。
其中包含了兩個子包:atomic以及lock,另外在concurrent下的阻塞隊列以及executors,這些就是concurrent包中的精華,之后會一一進行學習。而這些類的實現主要是依賴於volatile以及CAS,從整體上來看concurrent包的整體實現圖如下圖所示:
Lock簡介
我們下來看concurent包下的lock子包。鎖是用來控制多個線程訪問共享資源的方式,一般來說,一個鎖能夠防止多個線程同時訪問共享資源。在Lock接口出現之前,java程序主要是靠synchronized關鍵字實現鎖功能的,而java SE5之后,並發包中增加了lock接口,它提供了與synchronized一樣的鎖功能。雖然它失去了像synchronize關鍵字隱式加鎖解鎖的便捷性,但是卻擁有了鎖獲取和釋放的可操作性,可中斷的獲取鎖以及超時獲取鎖等多種synchronized關鍵字所不具備的同步特性。通常使用顯示使用lock的形式如下:
Lock lock = new ReentrantLock();
lock.lock();
try {
.......
} finally {
lock.unlock();
}
需要注意的是synchronized同步塊執行完成或者遇到異常是鎖會自動釋放,而lock必須調用unlock()方法釋放鎖,因此在finally塊中釋放鎖。
Lock接口API
我們現在就來看看lock接口定義了哪些方法:
//獲取鎖
void lock();
//獲取鎖的過程能夠響應中斷
void lockInterruptibly() throws InterruptedException;
//非阻塞式響應中斷能立即返回,獲取鎖放回true反之返回fasle
boolean tryLock();
//超時獲取鎖,在未超時內或者未中斷的情況下能夠獲取鎖
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
//釋放鎖
void unlock();
//獲取與lock綁定的等待通知組件,當前線程必須獲得了鎖才能進行等待,進行等待時會先釋放鎖,當再次獲取鎖時才能從等待中返回
Condition newCondition();
上面是lock接口下的六個方法,也只是從源碼中英譯中翻譯了一遍,感興趣的可以自己的去看看。那么在locks包下有哪些類實現了該接口了?先從最熟悉的ReentrantLock說起。
public class ReentrantLock implements Lock, java.io.Serializable
很顯然ReentrantLock實現了lock接口,接下來我們來仔細研究一下它是怎樣實現的。當你查看源碼時你會驚訝的發現ReentrantLock並沒有多少代碼,另外有一個很明顯的特點是:基本上所有的方法的實現實際上都是調用了其靜態內存類**Sync**
中的方法,而**Sync**
類繼承了**AbstractQueuedSynchronizer(AQS)**
。可以看出要想理解ReentrantLock關鍵核心在於對隊列同步器AbstractQueuedSynchronizer(簡稱同步器)的理解。
private final Sync sync;
/**
* Base of synchronization control for this lock. Subclassed
* into fair and nonfair versions below. Uses AQS state to
* represent the number of holds on the lock.
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
abstract void lock();
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return 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;
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
// Methods relayed from outer class
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}