新书Java并发编程系统与模型已上线,欢迎拜读。

前言

      barrier(屏障)与互斥量,读写锁,自旋锁不同,它不是用来保护临界区的。相反,它跟条件变量一样,是用来协同多线程一起工作的。
条件变量是多线程间传递状态的改变来达到协同工作的效果。屏障是多线程各自做自己的工作,如果某一线程完成了工作,就等待在屏障那里,直到其他线程的工作都完成了,再一起做别的事。举个通俗的例子:
      1.对于条件变量。在接力赛跑里,1号队员开始跑的时候,2,3,4号队员都站着不动,直到1号队员跑完一圈,把接力棒给2号队员,2号队员收到接力棒后就可以跑了,跑完再给3号队员。这里这个接力棒就相当于条件变量,条件满足后就可以由下一个队员(线程)跑。
      2.对于屏障:在百米赛跑里,比赛没开始之前,每个运动员都在赛场上自由活动,有的热身,有的喝水,有的跟教练谈论。比赛快开始时,准备完毕的运动员就预备在起跑线上,如果有个运动员还没准备完(除去特殊情况),他们就一直等,直到运动员都在起跑线上,裁判喊口号后再开始跑。这里的起跑线就是屏障,做完准备工作的运动员都等在起跑线,直到其他运动员也把准备工作做完。

      java.util.concurrent.CyclicBarrier类是一个同步机制。它可以通过一些算法来同步线程处理的过程。换言之,就是所有的线程必须等待对方,直到所有的线程到达屏障,然后继续运行。下面这个图可以说明:



                                                两个线程等待循环屏障


      两个线程通过调用CyclicBarrier的 await() 相互等待对方,一旦所有的线程都在CyclicBarrier中等待,然后所有的线程一起释放然后继续执行。

创建循环屏障

      当你要创建CyclicBarrier的时候必须指定在释放他们前有多少个线程等待,下面是一个例子:

CyclicBarrier barrier = new CyclicBarrier(2);复制代码

在CyclicBarrier等待

      下面是在CyclicBarrier处等待:

barrier.await();复制代码

      你也可以指线程等待的超时时间,当等待超时的时候,线程依然会被释放。即使并不是所有的线程都开始在CyclicBarrier等待。下面这行代码指定超时时间:

barrier.await(10, TimeUnit.SECONDS);复制代码

      所有线程在CyclicBarrier等待,是指:
       • 最后一个线程到达(调用await方法)

       • 一个线程被被另外一个线程中断(另外一个线程调用了这个现场的interrupt()方法)

       • 其中一个等待的线程被中断

       • 其中一个等待的线程超时

       • 一个外部的线程调用了yclicBarrier.reset()方法。

CyclicBarrier Action

      CyclicBarrier 支持一个Runnable屏障动作。当最后一个线程到达的时候,这个Runable对象就可以被执行。你需要将这个Runable屏障动作放置在他的构造器中,就像这样:

Runnable      barrierAction = ... ;
CyclicBarrier barrier       = new CyclicBarrier(2, barrierAction);复制代码

下面这个例子说明了如何使用CyclicBarrier:

Runnable barrier1Action = new Runnable() {
    public void run() {
        System.out.println("BarrierAction 1 executed ");
    }
};
Runnable barrier2Action = new Runnable() {
    public void run() {
        System.out.println("BarrierAction 2 executed ");
    }
};

CyclicBarrier barrier1 = new CyclicBarrier(2, barrier1Action);
CyclicBarrier barrier2 = new CyclicBarrier(2, barrier2Action);

CyclicBarrierRunnable barrierRunnable1 =
        new CyclicBarrierRunnable(barrier1, barrier2);

CyclicBarrierRunnable barrierRunnable2 =
        new CyclicBarrierRunnable(barrier1, barrier2);

new Thread(barrierRunnable1).start();
new Thread(barrierRunnable2).start();
Here is the CyclicBarrierRunnable class:复制代码

      下面是CyclicBarrierRunnable类:

public class CyclicBarrierRunnable implements Runnable{

    CyclicBarrier barrier1 = null;
    CyclicBarrier barrier2 = null;

    public CyclicBarrierRunnable(
            CyclicBarrier barrier1,
            CyclicBarrier barrier2) {

        this.barrier1 = barrier1;
        this.barrier2 = barrier2;
    }

    public void run() {
        try {
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() +
                                " waiting at barrier 1");
            this.barrier1.await();

            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() +
                                " waiting at barrier 2");
            this.barrier2.await();

            System.out.println(Thread.currentThread().getName() +
                                " done!");

        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }
}复制代码

      下面是控制台的输出。但是要注意有时候输出的顺序会发生变化,有时候是Thread-0先打印,有时候是Thread-1先打印。

Thread-0 waiting at barrier 1
Thread-1 waiting at barrier 1
BarrierAction 1 executed
Thread-1 waiting at barrier 2
Thread-0 waiting at barrier 2
BarrierAction 2 executed
Thread-0 done!
Thread-1 done!复制代码