CyclicBarrier是多线程中一个重要的类,主要用于线程组内部之间的线程的相互等待问题。
1.CyclicBarrier的工作原理
CyclicBarrier大致是可循环利用的屏障,顾名思义,这个名字也将这个类的特点给明确地表示出来了。首先,便是可重复利用,说明该类创建的对象可以复用;其次,屏障则体现了该类的原理:每个线程执行时,都会碰到一个屏障,直到所有线程执行结束,然后屏障便会打开,使所有线程继续往下执行。
这里介绍CyclicBarrier的两个构造函数:CyclicBarrier(int parties)和CyclicBarrier(int parties, Runnable barrierAction) :前者只需要声明需要拦截的线程数即可,而后者还需要定义一个等待所有线程到达屏障优先执行的Runnable对象。
实现原理:在CyclicBarrier的内部定义了一个Lock对象,每当一个线程调用await方法时,将拦截的线程数减1,然后判断剩余拦截数是否为初始值parties,如果不是,进入Lock对象的条件队列等待。如果是,执行barrierAction对象的Runnable方法,然后将锁的条件队列中的所有线程放入锁等待队列中,这些线程会依次的获取锁、释放锁。
举例说明:如果一个寝室四个人约好了去球场打球,由于四个人准备工作不同,所以约好在楼下集合,并且四个人集合好之后一起出发去球场。
package concurrent;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.;
public class CyclicBarrierDemo {
private static final ThreadPoolExecutor threadPool=new ThreadPoolExecutor(4,10,60,TimeUnit.SECONDS,new LinkedBlockingQueue());
//当拦截线程数达到4时,便优先执行barrierAction,然后再执行被拦截的线程。
private static final CyclicBarrier cb=new CyclicBarrier(4,new Runnable() {
public void run()
{
System.out.println(“寝室四兄弟一起出发去球场”);
}
});
private static class GoThread extends Thread{
private final String name;
public GoThread(String name)
{
this.name=name;
}
public void run()
{
System.out.println(name+“开始从宿舍出发”);
try {
Thread.sleep(1000);
cb.await();//拦截线程
System.out.println(name+“从楼底下出发”);
Thread.sleep(1000);
System.out.println(name+“到达操场”);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
catch(BrokenBarrierException e)
{
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
String[] str= {"李明","王强","刘凯","赵杰"};
for(int i=0;i<4;i++)
{
threadPool.execute(new GoThread(str[i]));
}
try
{
Thread.sleep(4000);
System.out.println("四个人一起到达球场,现在开始打球");
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
以上便是CyclicBarrier使用实例,通过await()方法对线程的拦截,拦截数加1,当拦截数为初始的parties,首先执行了barrierAction,然后对拦截的线程队列依次进行获取锁释放锁。接下来,在这个例子上讲解CyclicBarrier对象的复用特性。
package concurrent;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.;
public class CyclicBarrierDemo {
private static final ThreadPoolExecutor threadPool=new ThreadPoolExecutor(4,10,60,TimeUnit.SECONDS,new LinkedBlockingQueue());
//当拦截线程数达到4时,便优先执行barrierAction,然后再执行被拦截的线程。
private static final CyclicBarrier cb=new CyclicBarrier(4,new Runnable() {
public void run()
{
System.out.println(“寝室四兄弟一起出发去球场”);
}
});
private static class GoThread extends Thread{
private final String name;
public GoThread(String name)
{
this.name=name;
}
public void run()
{
System.out.println(name+“开始从宿舍出发”);
try {
Thread.sleep(1000);
cb.await();//拦截线程
System.out.println(name+“从楼底下出发”);
Thread.sleep(1000);
System.out.println(name+“到达操场”);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
catch(BrokenBarrierException e)
{
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
String[] str= {"李明","王强","刘凯","赵杰"};
String[] str1= {"王二","洪光","雷兵","赵三"};
for(int i=0;i<4;i++)
{
threadPool.execute(new GoThread(str[i]));
}
try
{
Thread.sleep(4000);
System.out.println("四个人一起到达球场,现在开始打球");
System.out.println("现在对CyclicBarrier进行复用.....");
System.out.println("又来了一拨人,看看愿不愿意一起打:");
}
catch(InterruptedException e)
{
e.printStackTrace();
}
//进行复用:
for(int i=0;i<4;i++)
{
threadPool.execute(new GoThread(str1[i]));
}
try
{
Thread.sleep(4000);
System.out.println("四个人一起到达球场,表示愿意一起打球,现在八个人开始打球");
//System.out.println("现在对CyclicBarrier进行复用");
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
3.CountDownLatch和CyclicBarrier的比较
1.CountDownLatch是线程组之间的等待,即一个(或多个)线程等待N个线程完成某件事情之后再执行;而CyclicBarrier则是线程组内的等待,即每个线程相互等待,即N个线程都被拦截之后,然后依次执行。
2.CountDownLatch是减计数方式,而CyclicBarrier是加计数方式。
3.CountDownLatch计数为0无法重置,而CyclicBarrier计数达到初始值,则可以重置。
4.CountDownLatch不可以复用,而CyclicBarrier可以复用。