CountDownLatch
CountDownLatch类利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。
CountDownLatch类只提供了一个构造器:
//参数count为计数值
public CountDownLatch(int count) { };
下面这3个方法是CountDownLatch类中最重要的方法:
//将count值减1
public void countDown() { };
//调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
public void await() throws InterruptedException { };
//和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };
使用案例:
public class TestCountDownLatch {
public static void main(String str[]){
try {
int threadNum=4;
final CountDownLatch latch = new CountDownLatch(threadNum);
for(int i=1;i<=threadNum;i++){
new Thread(new Job("线程:"+i,latch)).start();
}
System.out.println("等待子线程...");
latch.await();
System.out.println("子线程执行完毕...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Job implements Runnable {
private String threadName;
private CountDownLatch latch;
Job( String name,CountDownLatch latch) {
this.threadName = name;
this.latch=latch;
System.out.println("创建线程:" + threadName );
}
@Override
public void run() {
try {
System.out.println("运行线程:" + threadName );
Thread.sleep(2000);
System.out.println("结束线程:" + threadName);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
latch.countDown();
}
}
}
运行结果
创建线程:线程:1
创建线程:线程:2
创建线程:线程:3
创建线程:线程:4
等待子线程...
运行线程:线程:3
运行线程:线程:1
运行线程:线程:4
运行线程:线程:2
结束线程:线程:3
结束线程:线程:1
结束线程:线程:4
结束线程:线程:2
子线程执行完毕...
从上面结果可以看出,只有在所有子线程结束完成后,latch.await()之后主线程才会继续运行。
CyclicBarrier
字面意思回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。CyclicBarrier类位于java.util.concurrent包下,CyclicBarrier提供2个构造器:
public CyclicBarrier(int parties, Runnable barrierAction) {
}
public CyclicBarrier(int parties) {
}
参数parties指让多少个线程或者任务等待至barrier(障碍)状态;参数barrierAction为当这些线程都达到barrier状态时会执行的内容。然后CyclicBarrier中最重要的方法就是await方法,它有2个重载版本:
第一个版本比较常用,用来挂起当前线程,直至所有线程都到达barrier状态再同时执行后续任务。
public int await() throws InterruptedException, BrokenBarrierException { };
第二个版本是让这些线程等待至一定的时间,如果还有线程没有到达barrier状态就直接让到达barrier的线程执行后续任务。
public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };
使用案例:
public class TestCyclicBarrier {
static private CyclicBarrier cyclicBarrier;
public static void main(String str[]){
int threadNum=4;
cyclicBarrier=new CyclicBarrier(threadNum);
for(int i=1;i<=threadNum;i++){
new Thread(new CyclicBarrierJob("线程:"+i,cyclicBarrier)).start();
}
}
static class CyclicBarrierJob implements Runnable {
private String threadName;
private CyclicBarrier cyclicBarrier;
CyclicBarrierJob( String name,CyclicBarrier cyclicBarrier) {
this.threadName = name;
this.cyclicBarrier=cyclicBarrier;
System.out.println("创建" + threadName );
}
@Override
public void run() {
try {
System.out.println( threadName+"正在处理任务");
//暂停2秒,模拟完成任务所需时间
Thread.sleep(2000);
System.out.println(threadName+"完成任务");
cyclicBarrier.await();
} catch (BrokenBarrierException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("其他线程已经完成任务," + threadName+"结束线程");
}
}
}
执行结果:
创建线程:1
创建线程:2
创建线程:3
线程:1正在处理任务
创建线程:4
线程:4正在处理任务
线程:3正在处理任务
线程:2正在处理任务
线程:1完成任务
线程:4完成任务
线程:3完成任务
线程:2完成任务
其他线程已经完成任务,线程:1结束线程
其他线程已经完成任务,线程:4结束线程
其他线程已经完成任务,线程:3结束线程
其他线程已经完成任务,线程:2结束线程
从上面输出结果可以看出,每个线程执行完2s任务之后,就在等待其他线程执行任务完毕。当所有线程线程写入操作完毕之后,所有线程就继续进行后续的操作了。
如果想在所有线程写入操作完之后,进行额外的其他操作可以使用构造方法public CyclicBarrier(int parties, Runnable barrierAction)为CyclicBarrier提供Runnable参数:
public class TestCyclicBarrier {
static private CyclicBarrier cyclicBarrier;
public static void main(String str[]){
int threadNum=4;
cyclicBarrier = new CyclicBarrier(threadNum,new Runnable() {
@Override
public void run() {
System.out.println("当前线程是:"+Thread.currentThread().getName());
}
});
for(int i=1;i<=threadNum;i++){
new Thread(new CyclicBarrierJob("线程:"+i,cyclicBarrier)).start();
}
}
static class CyclicBarrierJob implements Runnable {
private String threadName;
private CyclicBarrier cyclicBarrier;
CyclicBarrierJob( String name,CyclicBarrier cyclicBarrier) {
this.threadName = name;
this.cyclicBarrier=cyclicBarrier;
System.out.println("创建" + threadName );
}
@Override
public void run() {
try {
System.out.println( threadName+"正在处理任务");
//暂停2秒,模拟完成任务所需时间
Thread.sleep(2000);
System.out.println(threadName+"完成任务");
cyclicBarrier.await();
} catch (BrokenBarrierException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("其他线程已经完成任务," + threadName+"结束线程");
}
}
}
当四个子线程都到达barrier状态后,会从四个线程中选择一个线程去执行Runnable。
public int await(long timeout, TimeUnit unit)设置超时等待时间,如:
cyclicBarrier.await(2000, TimeUnit.MILLISECONDS);
等待2秒后其他子线程还没有完成任务就抛出异常并继续执行下去。CyclicBarrier重用:
当初次的4个线程越过barrier状态后,又可以用来进行新一轮的使用。而CountDownLatch无法进行重复使用。
总结
CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同;
CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;
CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;
CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。