AQS簡介
AQS:AbstractQueuedSynchronizer,即隊列同步器。它是構建鎖或者其他同步組件的基礎框架(如ReentrantLock、ReentrantReadWriteLock、Semaphore等)。
同步器是用來構建鎖和其他同步組件的基礎框架,它的實現主要依賴一個int成員變量來表示同步狀態(tài)以及通過一個FIFO隊列構成等待隊列。它的子類必須重寫AQS的幾個protected修飾的用來改變同步狀態(tài)的方法,其他方法主要是實現了排隊和阻塞機制。AQS使用一個int類型的成員變量state來表示同步狀態(tài),當state>0時表示已經獲取了鎖,當state = 0時表示釋放了鎖。它提供了三個方法(getState()、setState(int newState)、compareAndSetState(int expect,int update))來對同步狀態(tài)state進行操作,當然AQS可以確保對state的操作是安全的。
子類被推薦定義為自定義同步組件的靜態(tài)內部類,同步器自身沒有實現任何同步接口,它僅僅是定義了若干同步狀態(tài)的獲取和釋放方法來供自定義同步組件的使用,同步器既支持獨占式獲取同步狀態(tài),也可以支持共享式獲取同步狀態(tài),這樣就可以方便的實現不同類型的同步組件。
同步器是實現鎖(也可以是任意同步組件)的關鍵,在鎖的實現中聚合同步器,利用同步器實現鎖的語義。可以這樣理解二者的關系:鎖是面向使用者,它定義了使用者與鎖交互的接口,隱藏了實現細節(jié);同步器是面向鎖的實現者,它簡化了鎖的實現方式,屏蔽了同步狀態(tài)的管理,線程的排隊,等待和喚醒等底層操作。鎖和同步器很好的隔離了使用者和實現者所需關注的領域。
AQS解決了子類實現同步器時涉及當的大量細節(jié)問題,例如獲取同步狀態(tài)、FIFO同步隊列。基于AQS來構建同步器可以帶來很多好處。它不僅能夠極大地減少實現工作,而且也不必處理在多個位置上發(fā)生的競爭問題。
在基于AQS構建的同步器中,只能在一個時刻發(fā)生阻塞,從而降低上下文切換的開銷,提高了吞吐量。同時在設計AQS時充分考慮了可伸縮行,因此J.U.C中所有基于AQS構建的同步器均可以獲得這個優(yōu)勢。
AQS通過內置的FIFO同步隊列來完成資源獲取線程的排隊工作,如果當前線程獲取同步狀態(tài)失敗(鎖)時,AQS則會將當前線程以及等待狀態(tài)等信息構造成一個節(jié)點(Node)并將其加入同步隊列,同時會阻塞當前線程,當同步狀態(tài)釋放時,則會把節(jié)點中的線程喚醒,使其再次嘗試獲取同步狀態(tài)。
AQS主要提供了如下一些方法:
- getState():返回同步狀態(tài)的當前值;
- setState(int newState):設置當前同步狀態(tài);
- compareAndSetState(int expect, int update):使用CAS設置當前狀態(tài),該方法能夠保證狀態(tài)設置的原子性;
- tryAcquire(int arg):獨占式獲取同步狀態(tài),獲取同步狀態(tài)成功后,其他線程需要等待該線程釋放同步狀態(tài)才能獲取同步狀態(tài);
- tryRelease(int arg):獨占式釋放同步狀態(tài);
- tryAcquireShared(int arg):共享式獲取同步狀態(tài),返回值大于等于0則表示獲取成功,否則獲取失敗;
- tryReleaseShared(int arg):共享式釋放同步狀態(tài);
- isHeldExclusively():當前同步器是否在獨占式模式下被線程占用,一般該方法表示是否被當前線程所獨占;
- acquire(int arg):獨占式獲取同步狀態(tài),如果當前線程獲取同步狀態(tài)成功,則由該方法返回,否則,將會進入同步隊列等待,該方法將會調用可重寫的tryAcquire(int arg)方法;
- acquireInterruptibly(int arg):與acquire(int arg)相同,但是該方法響應中斷,當前線程為獲取到同步狀態(tài)而進入到同步隊列中,如果當前線程被中斷,則該方法會拋出InterruptedException異常并返回;
- tryAcquireNanos(int arg,long nanos):超時獲取同步狀態(tài),如果當前線程在nanos時間內沒有獲取到同步狀態(tài),那么將會返回false,已經獲取則返回true;
- acquireShared(int arg):共享式獲取同步狀態(tài),如果當前線程未獲取到同步狀態(tài),將會進入同步隊列等待,與獨占式的主要區(qū)別是在同一時刻可以有多個線程獲取到同步狀態(tài);
- acquireSharedInterruptibly(int arg):共享式獲取同步狀態(tài),響應中斷;
- tryAcquireSharedNanos(int arg, long nanosTimeout):共享式獲取同步狀態(tài),增加超時限制;
- release(int arg):獨占式釋放同步狀態(tài),該方法會在釋放同步狀態(tài)之后,將同步隊列中第一個節(jié)點包含的線程喚醒;
- releaseShared(int arg):共享式釋放同步狀態(tài);
CLH同步隊列
AQS內部維護著一個FIFO隊列,該隊列就是CLH同步隊列。
CLH同步隊列是一個FIFO雙向隊列,AQS依賴它來完成同步狀態(tài)的管理,當前線程如果獲取同步狀態(tài)失敗時,AQS則會將當前線程已經等待狀態(tài)等信息構造成一個節(jié)點(Node)并將其加入到CLH同步隊列,同時會阻塞當前線程,當同步狀態(tài)釋放時,會把首節(jié)點喚醒(公平鎖),使其再次嘗試獲取同步狀態(tài)。
在CLH同步隊列中,一個節(jié)點表示一個線程,它保存著線程的引用(thread)、狀態(tài)(waitStatus)、前驅節(jié)點(prev)、后繼節(jié)點(next),其定義如下:
static final class Node {
/** 共享 */
static final Node SHARED = new Node();
/**獨占 */
static final Node EXCLUSIVE = null;
/** 因為超時或者中斷,節(jié)點會被設置為取消狀態(tài),被取消的節(jié)點時不會參與到競爭中的,他會一直保持取消狀態(tài)不會轉變?yōu)槠渌麪顟B(tài); */
static final int CANCELLED = 1;
/** 后繼節(jié)點的線程處于等待狀態(tài),而當前節(jié)點的線程如果釋放了同步狀態(tài)或者被取消,將會通知后繼節(jié)點,使后繼節(jié)點的線程得以運行 */
static final int SIGNAL = -1;
/** 節(jié)點在等待隊列中,節(jié)點線程等待在Condition上,當其他線程對Condition調用了signal()后,改節(jié)點將會從等待隊列中轉移到同步隊列 */
static final int CONDITION = -2;
/** 表示下一次共享式同步狀態(tài)獲取將會無條件地傳播下去 */
static final int PROPAGATE = -3;
/** 等待狀態(tài) */
volatile int waitStatus;
/** 前驅節(jié)點 */
volatile Node prev;
/** 后繼節(jié)點 */
volatile Node next;
/** 獲取同步狀態(tài)的線程 */
volatile Thread thread;
/**
* Link to next node waiting on condition, or the special
* value SHARED. Because condition queues are accessed only
* when holding in exclusive mode, we just need a simple
* linked queue to hold nodes while they are waiting on
* conditions. They are then transferred to the queue to
* re-acquire. And because conditions can only be exclusive,
* we save a field by using special value to indicate shared
* mode.
*/
Node nextWaiter;
/**
* Returns true if node is waiting in shared mode.
*/
final boolean isShared() {
return nextWaiter == SHARED;
}
/**
* Returns previous node, or throws NullPointerException if null.
* Use when predecessor cannot be null. The null check could
* be elided, but is present to help the VM.
*
* @return the predecessor of this node
*/
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
Node() { // Used to establish initial head or SHARED marker
}
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
CLH同步隊列入列
addWaiter(Node mode)
private Node addWaiter(Node mode) {
//新建Node
Node node = new Node(Thread.currentThread(), mode);
//快速嘗試添加尾節(jié)點
Node pred = tail;
if (pred != null) {
node.prev = pred;
//CAS設置尾節(jié)點
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//多次嘗試
enq(node);
return node;
}
enq(node)
private Node enq(final Node node) {
//死循環(huán)嘗試
for (;;) {
Node t = tail;
if (t == null) { // tail不存在,設置為首節(jié)點
if (compareAndSetHead(new Node()))
tail = head;
} else {
//設置為尾節(jié)點
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
在上面代碼中,兩個方法都是通過一個CAS方法compareAndSetTail(Node expect, Node update)來設置尾節(jié)點,該方法可以確保節(jié)點是線程安全添加的。在enq(Node node)方法中,AQS通過“死循環(huán)”的方式來保證節(jié)點可以正確添加,只有成功添加后,當前線程才會從該方法返回,否則會一直執(zhí)行下去。
CHL出列
CLH同步隊列遵循FIFO,首節(jié)點的線程釋放同步狀態(tài)后,將會喚醒它的后繼節(jié)點(next),而后繼節(jié)點將會在獲取同步狀態(tài)成功時將自己設置為首節(jié)點,這個過程非常簡單,head執(zhí)行該節(jié)點并斷開原首節(jié)點的next和當前節(jié)點的prev即可,注意在這個過程是不需要使用CAS來保證的,因為只有一個線程能夠成功獲取到同步狀態(tài)。過程圖如下:
AQS獨占鎖和共享鎖
獨占鎖
acquire(int arg)方法為AQS提供的模板方法,該方法為獨占式獲取同步狀態(tài),但是該方法對中斷不敏感,也就是說由于線程獲取同步狀態(tài)失敗加入到CLH同步隊列中,后續(xù)對線程進行中斷操作時,線程不會從同步隊列中移除。代碼如下:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
各個方法定義如下:
- tryAcquire:去嘗試獲取鎖,獲取成功則設置鎖狀態(tài)并返回true,否則返回false。該方法自定義同步組件自己實現,該方法必須要保證線程安全的獲取同步狀態(tài)。
- addWaiter:如果tryAcquire返回FALSE(獲取同步狀態(tài)失敗),則調用該方法將當前線程加入到CLH同步隊列尾部。
- acquireQueued:當前線程會根據公平性原則來進行阻塞等待(自旋),直到獲取鎖為止;并且返回當前線程在等待過程中有沒有中斷過。
- selfInterrupt:產生一個中斷。
獨占式同步狀態(tài)獲取
acquireQueued方法為一個自旋的過程,也就是說當前線程(Node)進入同步隊列后,就會進入一個自旋的過程,每個節(jié)點都會自省地觀察,當條件滿足,獲取到同步狀態(tài)后,就可以從這個自旋過程中退出,否則會一直執(zhí)行下去。如下:
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
//中斷標志
boolean interrupted = false;
/*
* 自旋過程,其實就是一個死循環(huán)而已
*/
for (;;) {
//當前線程的前驅節(jié)點
final Node p = node.predecessor();
//當前線程的前驅節(jié)點是頭結點,且同步狀態(tài)成功
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//獲取失敗,線程等待--具體后面介紹
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
從上面代碼中可以看到,當前線程會一直嘗試獲取同步狀態(tài),當然前提是只有其前驅節(jié)點為頭結點才能夠嘗試獲取同步狀態(tài),理由:
- 保持FIFO同步隊列原則。
-
頭節(jié)點釋放同步狀態(tài)后,將會喚醒其后繼節(jié)點,后繼節(jié)點被喚醒后需要檢查自己是否為頭節(jié)點。
acquire(int arg)方法流程
獨占式獲取響應中斷
AQS提供了acquire(int arg)方法以供獨占式獲取同步狀態(tài),但是該方法對中斷不響應,對線程進行中斷操作后,該線程會依然位于CLH同步隊列中等待著獲取同步狀態(tài)。為了響應中斷,AQS提供了acquireInterruptibly(int arg)方法,該方法在等待獲取同步狀態(tài)時,如果當前線程被中斷了,會立刻響應中斷拋出異常InterruptedException。
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
首先校驗該線程是否已經中斷了,如果是則拋出InterruptedException,否則執(zhí)行tryAcquire(int arg)方法獲取同步狀態(tài),如果獲取成功,則直接返回,否則執(zhí)行doAcquireInterruptibly(int arg)。doAcquireInterruptibly(int arg)定義如下:
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);
}
}
doAcquireInterruptibly(int arg)方法與acquire(int arg)方法僅有兩個差別。
- 方法聲明拋出InterruptedException異常
- 在中斷方法處不再是使用interrupted標志,而是直接拋出InterruptedException異常。
獨占式超時獲取
AQS除了提供上面兩個方法外,還提供了一個增強版的方法:tryAcquireNanos(int arg,long nanos)。該方法為acquireInterruptibly方法的進一步增強,它除了響應中斷外,還有超時控制。即如果當前線程沒有在指定時間內獲取同步狀態(tài),則會返回false,否則返回true。如下:
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}
tryAcquireNanos(int arg, long nanosTimeout)方法超時獲取最終是在doAcquireNanos(int arg, long nanosTimeout)中實現的,如下:
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
//nanosTimeout <= 0
if (nanosTimeout <= 0L)
return false;
//超時時間
final long deadline = System.nanoTime() + nanosTimeout;
//新增Node節(jié)點
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
//自旋
for (;;) {
final Node p = node.predecessor();
//獲取同步狀態(tài)成功
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return true;
}
/*
* 獲取失敗,做超時、中斷判斷
*/
//重新計算需要休眠的時間
nanosTimeout = deadline - System.nanoTime();
//已經超時,返回false
if (nanosTimeout <= 0L)
return false;
//如果沒有超時,則等待nanosTimeout納秒
//注:該線程會直接從LockSupport.parkNanos中返回,
//LockSupport為JUC提供的一個阻塞和喚醒的工具類,后面做詳細介紹
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
//線程是否已經中斷了
if (Thread.interrupted())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
針對超時控制,程序首先記錄喚醒時間deadline ,deadline = System.nanoTime() + nanosTimeout(時間間隔)。如果獲取同步狀態(tài)失敗,則需要計算出需要休眠的時間間隔nanosTimeout(= deadline - System.nanoTime()),如果nanosTimeout <= 0 表示已經超時了,返回false,如果大于spinForTimeoutThreshold(1000L)則需要休眠nanosTimeout ,如果nanosTimeout <= spinForTimeoutThreshold ,就不需要休眠了,直接進入快速自旋的過程。原因在于 spinForTimeoutThreshold 已經非常小了,非常短的時間等待無法做到十分精確,如果這時再次進行超時等待,相反會讓nanosTimeout 的超時從整體上面表現得不是那么精確,所以在超時非常短的場景中,AQS會進行無條件的快速自旋。
獨占式同步狀態(tài)釋放
當線程獲取同步狀態(tài)后,執(zhí)行完相應邏輯后就需要釋放同步狀態(tài)。AQS提供了release(int arg)方法釋放同步狀態(tài):
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
該方法同樣是先調用自定義同步器自定義的tryRelease(int arg)方法來釋放同步狀態(tài),釋放成功后,會調用unparkSuccessor(Node node)方法喚醒后繼節(jié)點。
共享鎖
共享式與獨占式的最主要區(qū)別在于同一時刻獨占式只能有一個線程獲取同步狀態(tài),而共享式在同一時刻可以有多個線程獲取同步狀態(tài)。例如讀操作可以有多個線程同時進行,而寫操作同一時刻只能有一個線程進行寫操作,其他操作都會被阻塞。
共享式同步狀態(tài)獲取
AQS提供acquireShared(int arg)方法共享式獲取同步狀態(tài):
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
//獲取失敗,自旋獲取同步狀態(tài)
doAcquireShared(arg);
}
從上面程序可以看出,方法首先是調用tryAcquireShared(int arg)方法嘗試獲取同步狀態(tài),如果獲取失敗則調用doAcquireShared(int arg)自旋方式獲取同步狀態(tài),共享式獲取同步狀態(tài)的標志是返回 >= 0 的值表示獲取成功。自選式獲取同步狀態(tài)如下:
private void doAcquireShared(int arg) {
/共享式節(jié)點
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//前驅節(jié)點
final Node p = node.predecessor();
//如果其前驅節(jié)點,獲取同步狀態(tài)
if (p == head) {
//嘗試獲取同步
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
tryAcquireShared(int arg)方法嘗試獲取同步狀態(tài),返回值為int,當其 >= 0 時,表示能夠獲取到同步狀態(tài),這個時候就可以從自旋過程中退出。
acquireShared(int arg)方法不響應中斷,與獨占式相似,AQS也提供了響應中斷、超時的方法,分別是:acquireSharedInterruptibly(int arg)、tryAcquireSharedNanos(int arg,long nanos),這里就不做解釋了。
阻塞和喚醒線程
在線程獲取同步狀態(tài)時如果獲取失敗,則加入CLH同步隊列,通過通過自旋的方式不斷獲取同步狀態(tài),但是在自旋的過程中則需要判斷當前線程是否需要阻塞,其主要方法在acquireQueued():
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
通過這段代碼我們可以看到,在獲取同步狀態(tài)失敗后,線程并不是立馬進行阻塞,需要檢查該線程的狀態(tài),檢查狀態(tài)的方法為 shouldParkAfterFailedAcquire(Node pred, Node node) 方法,該方法主要靠前驅節(jié)點判斷當前線程是否應該被阻塞,代碼如下:
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//前驅節(jié)點
int ws = pred.waitStatus;
//狀態(tài)為signal,表示當前線程處于等待狀態(tài),直接放回true
if (ws == Node.SIGNAL)
return true;
//前驅節(jié)點狀態(tài) > 0 ,則為Cancelled,表明該節(jié)點已經超時或者被中斷了,需要從同步隊列中取消
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
}
//前驅節(jié)點狀態(tài)為Condition、propagate
else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
這段代碼主要檢查當前線程是否需要被阻塞,具體規(guī)則如下:
- 如果當前線程的前驅節(jié)點狀態(tài)為SINNAL,則表明當前線程需要被阻塞,調用unpark()方法喚醒,直接返回true,當前線程阻塞
- 如果當前線程的前驅節(jié)點狀態(tài)為CANCELLED(ws > 0),則表明該線程的前驅節(jié)點已經等待超時或者被中斷了,則需要從CLH隊列中將該前驅節(jié)點刪除掉,直到回溯到前驅節(jié)點狀態(tài) <= 0 ,返回false
- 如果前驅節(jié)點非SINNAL,非CANCELLED,則通過CAS的方式將其前驅節(jié)點設置為SINNAL,返回false
如果 shouldParkAfterFailedAcquire(Node pred, Node node) 方法返回true,則調用parkAndCheckInterrupt()方法阻塞當前線程:
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
parkAndCheckInterrupt() 方法主要是把當前線程掛起,從而阻塞住線程的調用棧,同時返回當前線程的中斷狀態(tài)。其內部則是調用LockSupport工具類的park()方法來阻塞該方法。
當線程釋放同步狀態(tài)后,則需要喚醒該線程的后繼節(jié)點:
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//喚醒后繼節(jié)點
unparkSuccessor(h);
return true;
}
return false;
}
調用unparkSuccessor(Node node)喚醒后繼節(jié)點:
private void unparkSuccessor(Node node) {
//當前節(jié)點狀態(tài)
int ws = node.waitStatus;
//當前狀態(tài) < 0 則設置為 0
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
//當前節(jié)點的后繼節(jié)點
Node s = node.next;
//后繼節(jié)點為null或者其狀態(tài) > 0 (超時或者被中斷了)
if (s == null || s.waitStatus > 0) {
s = null;
//從tail節(jié)點來找可用節(jié)點
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
//喚醒后繼節(jié)點
if (s != null)
LockSupport.unpark(s.thread);
}
可能會存在當前線程的后繼節(jié)點為null,超時、被中斷的情況,如果遇到這種情況了,則需要跳過該節(jié)點,但是為何是從tail尾節(jié)點開始,而不是從node.next開始呢?原因在于node.next仍然可能會存在null或者取消了,所以采用tail回溯辦法找第一個可用的線程。最后調用LockSupport的unpark(Thread thread)方法喚醒該線程。
LockSupport
從上面我可以看到,當需要阻塞或者喚醒一個線程的時候,AQS都是使用LockSupport這個工具類來完成的。
LockSupport是用來創(chuàng)建鎖和其他同步類的基本線程阻塞原語
每個使用LockSupport的線程都會與一個許可關聯,如果該許可可用,并且可在進程中使用,則調用park()將會立即返回,否則可能阻塞。如果許可尚不可用,則可以調用 unpark 使其可用。但是注意許可不可重入,也就是說只能調用一次park()方法,否則會一直阻塞。
LockSupport定義了一系列以park開頭的方法來阻塞當前線程,unpark(Thread thread)方法來喚醒一個被阻塞的線程。如下:
public static void unpark(Thread var0) {
if (var0 != null) {
UNSAFE.unpark(var0);
}
}
park(Object blocker)方法的blocker參數,主要是用來標識當前線程在等待的對象,該對象主要用于問題排查和系統(tǒng)監(jiān)控。
park方法和unpark(Thread thread)都是成對出現的,同時unpark必須要在park執(zhí)行之后執(zhí)行,當然并不是說沒有不調用unpark線程就會一直阻塞,park有一個方法,它帶了時間戳(parkNanos(long nanos):為了線程調度禁用當前線程,最多等待指定的等待時間,除非許可可用)。
park()方法的源碼如下:
public static void park() {
UNSAFE.park(false, 0L);
}
unpark(Thread thread)方法源碼如下:
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
從上面可以看出,其內部的實現都是通過UNSAFE(sun.misc.Unsafe UNSAFE)來實現的,其定義如下:
public native void park(boolean var1, long var2);
public native void unpark(Object var1);
兩個都是native本地方法。Unsafe 是一個比較危險的類,主要是用于執(zhí)行低級別、不安全的方法集合。盡管這個類和所有的方法都是公開的(public),但是這個類的使用仍然受限,你無法在自己的java程序中直接使用該類,因為只有授信的代碼才能獲得該類的實例。