之所以把Latch與Barrier放在一起比較是因?yàn)樗麄兘o人一種相似的感覺(jué)。他們都是阻塞一些行為直至某個(gè)事件發(fā)生,但Latch是等待某個(gè)事件發(fā)生,而B(niǎo)arrier是等待線(xiàn)程。
先比較一下JCip中對(duì)二者的描述:
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。
即,閉鎖可以延遲線(xiàn)程執(zhí)行直至達(dá)到相應(yīng)的結(jié)束狀態(tài)。閉鎖就像一個(gè)大門(mén),未到達(dá)結(jié)束狀態(tài)相當(dāng)于大門(mén)緊閉,不讓任何線(xiàn)程通過(guò)。而到達(dá)結(jié)束狀態(tài)后,大門(mén)敞開(kāi),讓所有的線(xiàn)程通過(guò),但是一旦敞開(kāi)后不會(huì)再關(guān)閉。閉鎖可以用來(lái)確保一些活動(dòng)在某個(gè)事件發(fā)生后執(zhí)行。
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直譯為"柵欄",我也很喜歡這個(gè)叫法。柵欄可以使一組執(zhí)行在一處匯集,也就是說(shuō)我們可以用柵欄將一個(gè)問(wèn)題分解成多個(gè)獨(dú)立的子問(wèn)題,并在執(zhí)行結(jié)束后在同一處進(jìn)行匯集。當(dāng)線(xiàn)程到達(dá)匯集地后調(diào)用await,await方法會(huì)出現(xiàn)阻塞直至其他線(xiàn)程也到達(dá)匯集地。如果所有的線(xiàn)程都到達(dá)就可以通過(guò)柵欄,也就是所有的線(xiàn)程得到釋放,而且柵欄也可以被重新利用。
另外,下面javadoc中對(duì)二者之間區(qū)別的說(shuō)明:
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.
閉鎖從來(lái)都是帶著事件的觸發(fā)次數(shù)。await方法會(huì)一直阻塞至countDown方法將次數(shù)變成0為止,所有的線(xiàn)程被釋放才能進(jìn)行后續(xù)的工作。但這種現(xiàn)象只能出現(xiàn)一次,也就是說(shuō)觸發(fā)次數(shù)不會(huì)被重置。如果你想要一個(gè)可重置次數(shù)的閉鎖,那就用柵欄。
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.)
這種行為阻塞的典型用法之一就是將某個(gè)問(wèn)題分成多個(gè)部分,每個(gè)部分用不同的線(xiàn)程負(fù)責(zé),并記得減少閉鎖設(shè)置的次數(shù)。當(dāng)所有線(xiàn)程的工作結(jié)束后將通過(guò)await方法造成的阻塞,如果我們需要反復(fù)進(jìn)行這樣的工作就需要使用柵欄。
好了,既然Doug Lea老師將同一個(gè)觀點(diǎn)闡述了這么多遍,剩下就是放心大膽地使用了,也許我們將問(wèn)題想得太復(fù)雜了。下面貼出栗子,由一個(gè)startGate攔住所有線(xiàn)程的執(zhí)行,當(dāng)所有線(xiàn)程就緒完成后調(diào)用countDown將它們釋放,而另一扇大門(mén)——endGate后面正等著計(jì)算執(zhí)行時(shí)間,而endGate等待的事件由這些線(xiàn)程觸發(fā):
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; }}執(zhí)行看看:
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); } }));}接著試試柵欄,沒(méi)有搞任何復(fù)雜的東西,注意countSupposed%partyCount,主要是想用這個(gè)體現(xiàn)一下柵欄是否可以反復(fù)使用多次。如果countSupposed%partyCount的結(jié)果恰好為0,所有線(xiàn)程執(zhí)行結(jié)束后,主線(xiàn)程也會(huì)正常結(jié)束。反之則會(huì)一直阻塞下去,如果countSupposed%partyCount結(jié)果大于1且不為0,其結(jié)果就是我們看到"let's go to the barrier !!"出現(xiàn)的次數(shù):
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(); }}執(zhí)行:
public static void main(String[] args) { barrierTest(11, 20);}新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注