之所以把Latch與Barrier放在一起比較是因為他們給人一種相似的感覺。
他們都是阻塞一些行為直至某個事件發生,但Latch是等待某個事件發生,而Barrier是等待線程。
先比較一下JCIP中對二者的描述:
Latch
A latch is a synchronizer that can delay the progress of threads until it reaches its terminal state.
A latch acts as a gate: until the latch reaches the terminal state the gate is closed and no thread can pass, and in the terminal state the gate opens, allowing all threads to pass.
Once the latch reaches the terminal state, it cannot change state again, so it remains open forever.
Latches can be used to ensure that certain activities do not proceed until other one-time activities complete。
即,閉鎖可以延遲線程執行直至達到相應的結束狀態。閉鎖就像一個大門,未到達結束狀態相當于大門緊閉,不讓任何線程通過。
而到達結束狀態后,大門敞開,讓所有的線程通過,但是一旦敞開后不會再關閉。
閉鎖可以用來確保一些活動在某個事件發生后執行。
Barrier
CyclicBarrier allows a fixed number of parties to rendezvous repeatedly at a barrier point and is useful in parallel iterative algorithms that break down a problem into a fixed number of independent subproblems.
Threads call await when they reach the barrier point, and await blocks until all the threads have reached the barrier point.
If all threads meet at the barrier point, the barrier has been successfully passed, in which case all threads are released and the barrier is reset so it can be used again.
很多人都把Barrier直譯為"柵欄",我也很喜歡這個叫法。
柵欄可以使一組執行在一處匯集,也就是說我們可以用柵欄將一個問題分解成多個獨立的子問題,并在執行結束后在同一處進行匯集。
當線程到達匯集地后調用await,await方法會出現阻塞直至其他線程也到達匯集地。
如果所有的線程都到達就可以通過柵欄,也就是所有的線程得到釋放,而且柵欄也可以被重新利用。
另外,下面javadoc中對二者之間區別的說明:
A CountDownLatch is initialized with a given count.
The await methods block until the current count reaches zero due to invocations of the countDown method, after which all waiting threads are released and any subsequent invocations of await return immediately.
This is a one-shot phenomenon -- the count cannot be reset. If you need a version that resets the count, consider using a CyclicBarrier.
閉鎖從來都是帶著事件的觸發次數。
await方法會一直阻塞至countDown方法將次數變成0為止,所有的線程被釋放才能進行后續的工作。
但這種現象只能出現一次,也就是說觸發次數不會被重置。
如果你想要一個可重置次數的閉鎖,那就用柵欄。
Another typical usage would be to divide a problem into N parts, describe each part with a Runnable that executes that portion and counts down on the latch, and queue all the Runnables to an Executor.
When all sub-parts are complete, the coordinating thread will be able to pass through await.
(When threads must repeatedly count down in this way, instead use a CyclicBarrier.)
這種行為阻塞的典型用法之一就是將某個問題分成多個部分,每個部分用不同的線程負責,并記得減少閉鎖設置的次數。
當所有線程的工作結束后將通過await方法造成的阻塞,如果我們需要反復進行這樣的工作就需要使用柵欄。
好了,既然Doug Lea老師將同一個觀點闡述了這么多遍,剩下就是放心大膽地使用了,也許我們將問題想得太復雜了。
下面貼出栗子,由一個startGate攔住所有線程的執行,當所有線程就緒完成后調用countDown將它們釋放,而另一扇大門——endGate后面正等著計算執行時間,而endGate等待的事件由這些線程觸發:
public class TestHarness {
public static long timeTasks(int nThreads, final Runnable task)
throws InterruptedException {
final CountDownLatch startGate = new CountDownLatch(1);
final CountDownLatch endGate = new CountDownLatch(nThreads);
for (int i = 0; i < nThreads; i++) {
Thread t = new Thread() {
public void run() {
try {
startGate.await();
try {
task.run();
} finally {
endGate.countDown();
}
} catch (InterruptedException ignored) {
}
}
};
t.start();
}
long start = System.nanoTime();
startGate.countDown();
endGate.await();
long end = System.nanoTime();
return end - start;
}
}
執行看看:
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("cost :::"+TestHarness.timeTasks(10,new Runnable() {
@Override
public void run() {
int num = RandomUtils.nextInt(0,100);
if(num>50) try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Alvez ::"+ num);
}
}));
}
接著試試柵欄,沒有搞任何復雜的東西,注意countSupposed%partyCount,主要是想用這個體現一下柵欄是否可以反復使用多次。
如果countSupposed%partyCount的結果恰好為0,所有線程執行結束后,主線程也會正常結束。
反之則會一直阻塞下去,如果countSupposed%partyCount結果大于1且不為0,其結果就是我們看到"let's go to the barrier !!"出現的次數:
public static void barrierTest(int partyCount, int countSupposed) {
if(partyCount<1 || countSupposed < 1) throw new IllegalArgumentException();
final CyclicBarrier barrier = new CyclicBarrier(partyCount,new Runnable() {
@Override
public void run() {
System.out.println("let's go barrier !!");
}
});
System.out.println(countSupposed%partyCount==0?"let's show the smooth!!":"....but blocked");
for (int i = 0; i < countSupposed; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
barrier.await();
System.out.println(Thread.currentThread().getName() + " show!!");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
};
new Thread(runnable).start();
}
}
執行:
public static void main(String[] args) {
barrierTest(11, 20);
}