文章目錄
CountDownLatch
可用於實現閉鎖操作,
延遲線程的進度直到其到達終止狀態。確保某些活動直到其它活動都完成後才繼續運行,只開啓一次。發令槍響後,所有運動員跑步
//這個節點由於超時或中斷被取消了。節點不會離開(改變)這個狀態。尤其,一個被取消的線程不再會被阻塞了
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
/* 這個節點的後繼(或者即將被阻塞)被阻塞(通過park阻塞)了,所以當前節點需要喚醒它的後繼當它被釋放或者取消時。
爲了避免競爭,獲取方法必須首先表示他們需要一個通知信號,然後再原子性的嘗試獲取鎖,如果失敗,則阻塞。
也就是說,在獲取鎖的操作中,需要確保當前node的preNode的waitStatus狀態值爲’SIGNAL’,纔可以被阻塞,當
獲取鎖失敗時。(『shouldParkAfterFailedAcquire』方法的用意就是這)*/
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
/* 這個節點當前在一個條件隊列中。它將不會被用於當做一個同步隊列的節點直到它被轉移到同步隊列中,
轉移的同時狀態值(waitStatus)將會被設置爲0。
(這裏使用這個值將不會做任何事情與該字段其他值對比,只是爲了簡化機制)。*/
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
//一個releaseShared操作必須被廣播給其他節點。(只有頭節點的)該值會在doReleaseShared方法中被設置去確保持續的廣播,即便其他操作的介入。
static final int PROPAGATE = -3;
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
//構造一個用給定計數初始化的 CountDownLatch。
this.sync = new Sync(count);
}
Sync(int count) {
//設置同步狀態的值。
setState(count);
}
await
public void await() throws InterruptedException {
//以共享模式獲取對象,如果被中斷則中止。
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
//中斷檢測
if (Thread.interrupted())
throw new InterruptedException();
//個數是否消耗完,消耗完則直接跳過,直接執行
//否則執行下列方法
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
//將當前線程以及共享模式等待標記,存儲到尾端節點
final Node node = addWaiter(Node.SHARED);
//標記是否正常處理,如果出現中斷,則取消執行
boolean failed = true;
try {
for (;;) {
//返回前端節點
final Node p = node.predecessor();
//如果是當前頭節點
if (p == head) {
//判斷閉鎖是否是開的
int r = tryAcquireShared(arg);
//如果閉鎖是開的
if (r >= 0) {
setHeadAndPropagate(node, r);
//釋放GC
p.next = null; // help GC
//標記正確執行
failed = false;
return;
}
}
//檢查並修改一個節點的狀態,當該節點獲取鎖失敗時。返回true如果線程需要阻塞。
if (shouldParkAfterFailedAcquire(p, node) &&
//這裏執行阻塞
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
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;
if (pred != null) {
//將當前節點添加到尾部節點
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//設置尾部節點,以及節點初始化
enq(node);
return node;
}
private Node enq(final Node node) {
//(自旋+CAS)
for (;;) {
Node t = tail;
//初始化,頭和尾爲空節點
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
///設置尾部節點
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
//設置當前節點爲頭節點,以及清空當前線程和前置節點
setHead(node);
//如果閉鎖是開的,且頭爲null,且waitStatus
//如果標識了廣播(propagate>0),
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
//如果當前節點爲null或者當前節點爲共享等待標記,則釋放,執行
//enq初始化時,會有短暫爲null現象
if (s == null || s.isShared())
doReleaseShared();
}
}
private void doReleaseShared() {
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
//更新默認值0,且解除park
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
//則說明node的前驅節點已經被要求去通知釋放它的後繼節點,所以node可以安全的被掛起(park)
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
//則說明node的前驅節點被取消了。那麼跳過這個前驅節點並重新標誌一個有效的前驅節點(即,
// waitStatus <= 0 的節點可作爲有效的前驅節點),然後,退出方法,返回false。
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//其他情況下,即pred.waitStatus爲’0’或’PROPAGATE’。
// 表示我們需要一個通知信號(即,當前的node需要喚醒的通知),
// 但是當前還不能掛起node。
// 調用『compareAndSetWaitStatus(pred, ws, Node.SIGNAL)』方法通過CAS的方式來修改前驅節點的waitStatus爲“SIGNAL”。
// 退出方法,返回false。
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
//等待狀態值指示後續線程需要斷開連接
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
private final boolean parkAndCheckInterrupt() {
//爲了線程調度,在許可可用之前禁用當前線程。
LockSupport.park(this);
//測試當前線程是否已經中斷。
return Thread.interrupted();
}
countDown
public void countDown() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
//檢測閉鎖是否開啓
if (tryReleaseShared(arg)) {
//釋放
doReleaseShared();
return true;
}
return false;
}
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
//修改c=c-1,當=0時,直接返回
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
private void doReleaseShared() {
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
//廣播釋放所有阻塞的鎖
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
//更新默認值0,且解除park
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
//重置狀態值
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
Node s = node.next;
//如果下一個節點爲null,且狀態取消了,則從尾端遍歷,查找未取消的線程
//所以當看到next字段爲null時並不意味着當前節點是隊列的尾部了。
//無論如何,如果一個next字段顯示爲null,我們能夠從隊列尾向前掃描進行復核。
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
釋放後,這裏的阻塞方法會解除阻塞,向下執行
爲什麼node.next可能爲null?
cancelAcquire
private void cancelAcquire(Node node) {
// Ignore if node doesn't exist
if (node == null)
return;
node.thread = null;
// Skip cancelled predecessors
//將node的prev屬性指向一個在它之前的有效的節點
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
// predNext is the apparent node to unsplice. CASes below will
// fail if not, in which case, we lost race vs another cancel
// or signal, so no further action is necessary.
/* 這個predNext是pred表面上的下一個連接的節點(即,無需考慮該節點是否被取消了)。
下面的CAS操作將會失敗(『compareAndSetNext(pred, predNext, null);』or『compareAndSetNext(pred, predNext, next);』)
,如果和其他的取消或通知操作發生競爭時,這時不需要進一步的操作。因爲如果產生競爭,
說明pred的next已經被修改了,並且是最新的值了,而我們的操作也就沒有要執行的必要了。*/
Node predNext = pred.next;
// Can use unconditional write instead of CAS here.
// After this atomic step, other Nodes can skip past us.
// Before, we are free of interference from other threads.
/* 將node的waitStatus設置爲’CANCELLED’。這裏可以使用無條件的寫代替CAS(注意,node的waitStatus是volatile的)。
在這個原子操作之後,其他節點會跳過我們(即,跳過waitStatus被置位CANCELLED的節點),
在這個原子操作之前,我們不受其他線程的干擾。也就是說,無論其他線程對node的waitStatus是否有在操作,
在當前的情況下我們都需要將這個node的waitStatus置爲’CANCELLED’。*/
node.waitStatus = Node.CANCELLED;
// If we are the tail, remove ourselves.
/* 如果待取消的node節點是隊列尾節點的話(即,『node == tail』),那麼刪除node自己即可。使用CAS將tail節點設置成前面得到的第一個有效前驅節點
(即,『compareAndSetTail(node, pred)』)。並且CAS操作成功的話,
執行『compareAndSetNext(pred, predNext, null);』
也就是將tail的next置爲null的意思。如果該CAS操作失敗的話,
沒關係。說明此時tail已經被修改了。*/
if (node == tail && compareAndSetTail(node, pred)) {
compareAndSetNext(pred, predNext, null);
} else {
// If successor needs signal, try to set pred's next-link
// so it will get one. Otherwise wake it up to propagate.
int ws;
if (
//pred不是head節點
pred != head &&
//pred.waitStatus爲SIGNAL” 或者
((ws = pred.waitStatus) == Node.SIGNAL ||
//“pred.waitStatus <= 0”時且通過CAS將pred.waitStatus設置爲SIGNAL”成功
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
//pred的thread非空
pred.thread != null) {
Node next = node.next;
/* 當node的next節點非空,且next節點的waitStatus<=0(說明next節點未被取消)時,
通過CAS將pred的next執行node的next(即,pred.next = node.next)。
同時,如果該CAS操作失敗是沒關係的,說明有其他線程操作已經修改了該pre的next值。*/
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
//釋放當前這個待取消節點的下一個節點。
//當prev是head節點,或者prev也被取消的話,會執行『unparkSuccessor(node);』來釋放node的下一個節點,其實也就是pred的下一個節點)
unparkSuccessor(node);
}
//釋放GC
node.next = node; // help GC
}
}
總結
依次按照鏈表順序執行,當設置共享模式的時候,廣播喚醒,
喚醒的節點中途取消了,則從尾端遍歷尋找下一個可以喚醒的節點
FutureTask
提前加載稍後需要的數據
Future表示異步計算的結果。它提供了檢查計算是否完成的方法,以等待計算的完成,並獲取計算的結果。計算完成後只能使用 get 方法來獲取結果,如有必要,計算完成前可以阻塞此方法。
Callable返回結果並且可能拋出異常的任務。實現者定義了一個不帶任何參數的叫做 call 的方法。
/**
* The run state of this task, initially NEW. The run state
* transitions to a terminal state only in methods set,
* setException, and cancel. During completion, state may take on
* transient values of COMPLETING (while outcome is being set) or
* INTERRUPTING (only while interrupting the runner to satisfy a
* cancel(true)). Transitions from these intermediate to final
* states use cheaper ordered/lazy writes because values are unique
* and cannot be further modified.
*
* Possible state transitions:
* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED
*/
private volatile int state;
//表示是個新的任務或者還沒被執行完的任務。這是初始狀態。
private static final int NEW = 0;
//任務已經執行完成或者執行任務的時候發生異常,但是任務執行結果或者異常原因還沒有保存到outcome字段
// (outcome字段用來保存任務執行結果,如果發生異常,則用來保存異常原因)的時候,
// 狀態會從NEW變更到COMPLETING。但是這個狀態會時間會比較短,屬於中間狀態。
private static final int COMPLETING = 1;
//任務已經執行完成並且任務執行結果已經保存到outcome字段,狀態會從COMPLETING轉換到NORMAL。這是一個最終態。
private static final int NORMAL = 2;
//任務執行發生異常並且異常原因已經保存到outcome字段中後,狀態會從COMPLETING轉換到EXCEPTIONAL。這是一個最終態。
private static final int EXCEPTIONAL = 3;
//任務還沒開始執行或者已經開始執行但是還沒有執行完成的時候,用戶調用了cancel(false)方法取消任務且不中斷任務執行線程,
// 這個時候狀態會從NEW轉化爲CANCELLED狀態。這是一個最終態。
private static final int CANCELLED = 4;
//任務還沒開始執行或者已經執行但是還沒有執行完成的時候,
// 用戶調用了cancel(true)方法取消任務並且要中斷任務執行線程但是還沒有中斷任務執行線程之前,
// 狀態會從NEW轉化爲INTERRUPTING。這是一箇中間狀態。
private static final int INTERRUPTING = 5;
//調用interrupt()中斷任務執行線程之後狀態會從INTERRUPTING轉換到INTERRUPTED。這是一個最終態。
private static final int INTERRUPTED = 6;
構造
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
//設置回調函數,返回value
this.callable = callable;
//初始狀態
this.state = NEW; // ensure visibility of callable
}
帶返回構造
public FutureTask(Runnable runnable, V result) {
//任務執行成果,返回result
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
//適配器,底層包裝task
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
//返回設置結果
return result;
}
}
run
因爲繼承了runnable接口,所以使用線程時,直接調用run
public void run() {
//當前狀態不是新建,說明已經執行過或者取消了,直接返回
if (state != NEW ||
// 狀態爲新建,則嘗試添加當前線程到runner中,如果失敗直接返回
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
//回調函數有值且狀態新建
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//調用回調函數,保存值到result中
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
//異常處理
setException(ex);
}
//正確執行,則賦值
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
//任務中斷,中斷執行
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
protected void setException(Throwable t) {
//將當前狀態 NEW => COMPLETING
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
//異常保存到outcome中
outcome = t;
//將當前狀態COMPLETING => EXCEPTIONAL
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
protected void set(V v) {
// NEW => COMPLETING
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
// outcome保存返回結果
outcome = v;
//COMPLETING => NORMAL
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
get
public V get() throws InterruptedException, ExecutionException {
int s = state;
//還在執行,則阻塞等待
if (s <= COMPLETING)
s = awaitDone(false, 0L);
//返回結果
return report(s);
}
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
//計算阻塞時間
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) {
//對中斷的處理
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
int s = state;
//已經取消或者出現異常,則清空線程,返回值
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING) // cannot time out yet
//正在執行則暫停當前線程,並允許執行其他線程
Thread.yield();
else if (q == null)
//創建等待節點
q = new WaitNode();
else if (!queued)
//當前節點添加到頭
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) {
//計算超時時間
nanos = deadline - System.nanoTime();
//對超時的處理
if (nanos <= 0L) {
//刪除節點,返回狀態值
removeWaiter(q);
return state;
}
//阻塞等待特定時間
LockSupport.parkNanos(this, nanos);
}
else
//阻塞等待直到被其他線程喚醒
LockSupport.park(this);
}
}
private void removeWaiter(WaitNode node) {
if (node != null) {
//清空當前線程
node.thread = null;
retry:
for (;;) { // restart on removeWaiter race
for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
s = q.next;
// pred => q => s
if (q.thread != null)
pred = q;
else if (pred != null) {
//刪除中間thread爲null的數據
//當q.thread=null且,pred不爲null,則刪除q節點
pred.next = s;
if (pred.thread == null) // check for race
continue retry;
}
else if (!
//刪除waiters節點的頭數據,並更新爲waiters.next
//當q.thread=null且,pred爲null,則替換爲s節點,如果q爲waiters節點,且修改失敗,則跳過當前循環,繼續執行下一個循環
UNSAFE.compareAndSwapObject(this, waitersOffset,
q, s))
continue retry;
}
break;
}
}
}
private V report(int s) throws ExecutionException {
//獲取結果值,有可能是異常
Object x = outcome;
//執行完畢,則返回結果
if (s == NORMAL)
return (V)x;
//異常或取消,則拋出異常
if (s >= CANCELLED)
throw new CancellationException();
//拋出異常
throw new ExecutionException((Throwable)x);
}
cancel
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(
//如果任務已經結束
state == NEW &&
//且將 NEW => mayInterruptIfRunning需要中斷則設置爲 INTERRUPTING 否則 CANCELLED
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
//直接返回
return false;
try { // in case call to interrupt throws exception
//需要中斷
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
//中斷
t.interrupt();
} finally { // final state
//修改狀態爲INTERRUPTED
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}
finishCompletion
private void finishCompletion() {
//成功,異常,中斷的時候會調用
// assert state > COMPLETING;
//依次遍歷waiters鏈表,喚醒節點中的線程,然後把callable置空。
for (WaitNode q; (q = waiters) != null;) {
//清空waiters
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
//清空線程,且喚醒
q.thread = null;
//喚醒
LockSupport.unpark(t);
}
WaitNode next = q.next;
//到達邊界,則跳出
if (next == null)
break;
//q更新爲下一個節點
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
done();
callable = null; // to reduce footprint
}
總結
每次涉及到成功,異常,中斷的時候會喚醒所有等待的線程
最後執行的會先添加到等待線程頭節點中.也就是最先喚醒的時最後執行的線程
內部維護7個狀態,分別監控執行,取消,中斷3個執行過程.
Semaphore
計數信號量:控制同時訪問某個特定資源的操作數量,或者同時執行某個指定操作的數量,實現某種資源池,對容器施加邊界
acquire
步驟同CountDownLatch.wait
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
final int nonfairTryAcquireShared(int acquires) {
//<0才阻塞
for (;;) {
//獲取同步狀態的值。
int available = getState();
int remaining = available - acquires;
//available爲0或者更新成功,直接返回
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
release
public void release() {
sync.releaseShared(1);
}
protected final boolean tryReleaseShared(int releases) {
for (;;) {
//獲取同步狀態的值。
int current = getState();
//累加
int next = current + releases;
//內存溢出,也就是超過了Integer範圍上限
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
//修改值返回
if (compareAndSetState(current, next))
return true;
}
}
總結
與閉鎖和其類似,閉鎖是爲0,纔可以訪問,信號量恰恰相反,
信號量和閉鎖的區別是不是一次性的,可以重複限定多少線程可以訪問資源.
如果不限定釋放的條件,可以不停的釋放從而超過構造設置的許可個數,
建議這樣使用
public boolean add(T o) throws InterruptedException {
//阻塞獲得許可
sem.acquire();
boolean wasAdded = false;
try {
wasAdded = set.add(o);
return wasAdded;
} finally {
//返回許可給信號量
if (!wasAdded)
sem.release();
}
}
public boolean remove(Object o) {
boolean wasRemoved = set.remove(o);
if (wasRemoved)
sem.release();
return wasRemoved;
}
CyclicBarrier
阻塞一組線程直到某件事發生,可重複使用,另一種柵欄式。5個人約好集合後去其他地方。
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
//屏障攔截的線程數量
this.parties = parties;
//線程數量
this.count = parties;
//指定個數線程被wait後,執行的後續方法
this.barrierCommand = barrierAction;
}
await
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
```java
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
//鎖
lock.lock();
try {
final Generation g = generation;
//當前柵欄是否損壞
if (g.broken)
throw new BrokenBarrierException();
//線程中斷處理
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
//獲取下標
int index = --count;
//說明最後一個調用了
if (index == 0) { // tripped
boolean ranAction = false;
try {
//獲取柵欄任務
final Runnable command = barrierCommand;
if (command != null)
//執行
command.run();
ranAction = true;
//重新構造可wait線程數
//以及換代
nextGeneration();
return 0;
} finally {
//非正常退出
if (!ranAction)
//標記柵欄損壞
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
for (;;) {
try {
//沒有設置超時,則一直阻塞
if (!timed)
//等待的時候,會釋放鎖
trip.await();
else if (nanos > 0L)
//阻塞超時時間
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
//當代未變,且沒標記損壞
if (g == generation && ! g.broken) {
//柵欄損壞
breakBarrier();
throw ie;
} else {
//說明這個線程不是當代的,就不會影響當代執行,則中斷線程
//如果在等待過程中,線程被中斷了,就拋出異常。但如果中斷的線程所對應的CyclicBarrier不是這代的,
// 比如,在最後一次線程執行signalAll後,並且更新了這個“代”對象。在這個區間,
// 這個線程被中斷了,那麼,JDK認爲任務已經完成了,就不必在乎中斷了,只需要打個標記。
// 該部分源碼已在dowait(boolean, long)方法中進行了註釋
Thread.currentThread().interrupt();
}
}
if (g.broken)
throw new BrokenBarrierException();
//表示正常換代了,返回當前線程所在下標,便於下次做選舉等操作
//仔細發現,柵欄不是一次性的,可以重複使用多次,但是鎖是同一個,所以需要用generation進行區分,是否是同一個柵欄
if (g != generation)
return index;
//超時,標記柵欄損壞,且拋出異常
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
//釋放鎖
lock.unlock();
}
}
private void nextGeneration() {
// signal completion of last generation
//喚醒所有線程
trip.signalAll();
// set up next generation
//count恢復parties個數
count = parties;
//換代
generation = new Generation();
}
private void breakBarrier() {
//標記柵欄損壞
generation.broken = true;
//count恢復parties個數
count = parties;
//喚醒所有線程
trip.signalAll();
}
reset
public void reset() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
breakBarrier(); // break the current generation
nextGeneration(); // start a new generation
} finally {
lock.unlock();
}
}
總結:可重複使用多次,底層採用顯示鎖進行加鎖,
索引按照初始count遞減,當索引爲0時,執行柵欄任務
當執行成功,通知當代所有線程,接着換代,如果當代有線程中斷了,且已經執行了換代任務了,這個時候會將中斷線程標記中斷
Exchanger
2方柵欄(Exchange):一個人有零食,另一個人有錢,他們兩個想等價交換,對好口號在某個地方相見,一個人先到了之後,必須等另一個人帶着需要的東西來了之後,才能開始交換。