CyclicBarrier(循环屏障):当多个线程同时执行时,在某个步骤完成时,需要互相等待共同完成,然后在执行时,就可以使用该类.该类的作用就是会实现多个类达到一起行动的作用.
1,构造器:该类有两个构造器,第一个参数是设置有多少条线程数需要等待,然后共同达到某个等待点,第二个参数是传入一个Runnable,当第一个线程到达屏障点的时候,会先执行该接口里的run方法,在后面会在源码里说明.
主要使用方法:await(),里面执行的dowait()
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Generation g = generation;
//初始值broken是false
if (g.broken)
throw new BrokenBarrierException();
//如果该线程被中断过则抛出异常
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
//count是计数器,是传入的同步的线程数
int index = --count;
//当计数为0时,则先执行Runable接口的方法,然后唤醒所有线程,同时也说明所有线程都到达屏障点了,执行里面唤醒线程的方法
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
//该方法会唤醒所有等待的线程,重置generation变量,使得后面还能继续设置屏障
nextGeneration();
return 0;
} finally {
if (!ranAction)
//该方法会终止屏障,唤醒在屏障出等待的线程
breakBarrier();
}
}
// loop until tripped, broken, interrupted, or timed out
//通过自旋锁实现线程在屏障出等待的功能
for (;;) {
try {
if (!timed)
//condition的wait方法使得调用的线程进入等待状态
trip.await();
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
Thread.currentThread().interrupt();
}
}
//判断是否被中断屏蔽,是的抛出异常
if (g.broken)
throw new BrokenBarrierException();
//判断是否被通知屏蔽结束,所有线程都到达屏障点了
if (g != generation)
return index;
//如果设定时间了,判断是否超时
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
通过对该方法的分析,大体知道该类的实现原理了.
案例:
import java.util.concurrent.CyclicBarrier;
class CyclicBarrierDemo {
/**
* CyclicBarrier:关卡
* 它约束多线程必需同时达到某时刻,同时向下执行,在没有达到之前保持等待
* 比如:周末约定去爬山,在公司门口集合,必须要等所有人都到后大家在一起出发。
*/
static void test(int threadNum) {
// CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
// 构造方法可支持一个Runnable,用于在所有线程都达到后首先执行。
final CyclicBarrier cyclicBarrier = new CyclicBarrier(3, new Runnable() {
@Override
public void run() {
System.out.println("都准备好了!");
}
});
for (int i = 0; i < threadNum; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("线程" + Thread.currentThread().getName() + " 已经准备好.");
cyclicBarrier.await();
Thread.sleep((long) (Math.random() * 2000));
System.out.println("线程" + Thread.currentThread().getName() + " 处理完毕,汇报结果!");
} catch (Exception e) {}
}
}).start();
}
}
}
public class Test {
public static void main(String[] args) {
CyclicBarrierDemo.test(3);
}
}
CountDownLatch:该类和CyclicBarrier类功能类似,都是设置屏障使多个线程达到在某个点后,互相等待,都到达时在唤醒其他等待线程,共同执行下面的业务逻辑.但是该类能设置一次屏障.举例:比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。
1构造器:传入的参数是计数器,显示要等待多少个线程执行完任务
2主要方法:
public void await() throws InterruptedException { }; //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public void countDown() { }; //将count值减1
public class Test {
public static void main(String[] args) {
final CountDownLatch latch = new CountDownLatch(2);
new Thread(){
public void run() {
try {
System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
Thread.sleep(3000);
System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
new Thread(){
public void run() {
try {
System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
Thread.sleep(3000);
System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
try {
System.out.println("等待2个子线程执行完毕...");
latch.await();
System.out.println("2个子线程已经执行完毕");
System.out.println("继续执行主线程");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
总结:从两个案例中还是可以看出两个的用途还有区别的,一个可以多次设置屏障等待,一个值能一次.而CountDownLatch主要是针对指定线程,去等待其他先,CyclicBarrier是多个线程没有指定线程,哪个线程先到屏障点,该线程就先等待,当所有线程都完成了,那在唤醒等待线程去继续运行
两个类主要的方法都使用的是AQS类里面的方法.以后可详细说明,待后面学习.