CyclicBarrier是java推出的一个并发编程工具,它用在多个线程之间协同工作。线程约定到达某个点,到达这个点之后的线程都停下来,直到最后一个线程也到达了这个点之后,所有的线程才会得到释放。常用的场景是:多个worker线程,每个线程都在循环地做一部分工作,并在最后用cyclicBarrier.await()设下约定点,当最后一个线程做完了工作也到达约定点后,所有线程得到释放,开始下一轮工作。也就是下面这样: 1 while(!done()){ 2 //working 3 cyclicBarrier.await(); 4 } CyclicBarrier还支持一个回调函数,每当一轮工作结束后,下一轮工作开始前,这个回调函数都会被调用一次。 但是,使用CyclicBarrier必须准守最佳实践的使用方法,否则,就可能达不到想要的效果。比如,下面这样,就是一种典型的错误使用方法: private void process(CyclicBarrier cyclicBarrier) { final int n = 100; Runnable worker= new Runnable() { @Override public void run() { try { //模拟工作 Thread.sleep(3000); } catch (InterruptedException ex) { ex.printStackTrace(); } try { cyclicBarrier.await(); } catch (BrokenBarrierException | InterruptedException ex) { ex.printStackTrace(); } } System.out.println("Worker is done"); System.out.println("Thread of Worker is " Thread.currentThread().getId()); }; for (int i = 0; i < n; i ) { Thread t1 = new Thread(worker); Thread t2 = new Thread(worker); t1.start(); t2.start(); } } 在上面的代码中,工作不在worker线程中循环,而是在开启工作的线程中循环,也就是说,它会不断地开启新的worker线程。这会导致的一个问题是,上一轮的回调还没执行完成,下一轮的工作就已经开始了。 那么为什么呢?下面来分析一下原因。 首先,要知道CyclicBarrier是如何做到在上一轮工作结束后下一轮工作开始前执行回调函数的。查看jdoc稳定,里面有这么一句话“A CyclicBarrier supports an optional 然后,我们再来看看上面错误的使用方法。在错误的使用方法中,主线程的每一轮循环中都开启了新的worker线程,这样在回调函数结束之前,前面开启的worker线程确实没有得到释放,但是,新开启的工作线程却完全可以执行下一轮工作,这就是为什么在回调函数执行完毕之前,新一轮的工作就已经开始了的原因。并且,错误方法中的每一个工作线程只执行一轮工作就结束了,每一轮工作之间的线程互不影响,这也就失去了协作性,因此,千万要避免写出这种代码。 关于CyclicBarrier使用的最佳时间,基本上就是官方示例中的用法了,如下: 1 class Solver { 2 final int N; 3 final float[][] data; 4 final CyclicBarrier barrier; 5 6 class Worker implements Runnable { 7 int myRow; 8 Worker(int row) { myRow = row; } 9 public void run() { 10 while (!done()) { 11 processRow(myRow); 12 13 try { 14 barrier.await(); 15 } catch (InterruptedException ex) { 16 return; 17 } catch (BrokenBarrierException ex) { 18 return; 19 } 20 } 21 } 22 } 23 24 public Solver(float[][] matrix) { 25 data = matrix; 26 N = matrix.length; 27 barrier = new CyclicBarrier(N, 28 new Runnable() { 29 public void run() { 30 mergeRows(...); 31 } 32 }); 33 for (int i = 0; i < N; i) 34 new Thread(new Worker(i)).start(); 35 36 waitUntilDone(); 37 } 38 } 最后在有一个问题是,回调函数是在哪一个线程里执行的? 根据我的demo测试发现,是在第一个到达的线程中执行的。当然,官方并没有明确规定这一点,也许以后会有变化吧,所以,我们也不能以来这一特征。我的demo如下: public class Demo1 { public static main(String[] args){ Demo1 demo = new Demo1(); demo1.showInfThreadWhenDirectly(); } 输出结果如下: [Directly] main Thread is 1 [Directly] Thread in invert call function is10 first arrival Thread in this iteration is: 10 [Directly] Thread in invert call function is10 first arrival Thread in this iteration is: 10 [Directly] Thread in invert call function is10 first arrival Thread in this iteration is: 10 [Directly] Thread in invert call function is10 first arrival Thread in this iteration is: 10 [Directly] Thread in invert call function is11 first arrival Thread in this iteration is: 11 [Directly] Thread in invert call function is10 first arrival Thread in this iteration is: 10 [Directly] Thread in invert call function is10 first arrival Thread in this iteration is: 10 [Directly] Thread in invert call function is10 first arrival Thread in this iteration is: 10 [Directly] Thread in invert call function is11 first arrival Thread in this iteration is: 11
另外,官方还有一段:“
意思是说,如果回调动作“arrier action”不需要在所有工作线程都停止的状态下执行的话,那么可以随便找一个工作线程去做这个动作。为了支持这个,CyclicBarrier 的await( )方法有一个返回值,返回的就是当前线程是第几个到达约定点(barrier)的。 来源:http://www./content-4-162451.html |
|