多线程间经常需要协调一批任务线程同时完成之后去做某事,而Java中提供的流程控制有两种方式:一种是CyclicBarrier,另一种是CountDownLatch.
第一种方式 CyclicBarrier。来自jdk的解释:一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。 CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作 很有用。
第二种方式 CountDownLatch。来自jdk的解释:一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。 用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。如果需要重置计数,请考虑使用 CyclicBarrier。 CountDownLatch 是一个通用同步工具,它有很多用途。将计数 1 初始化的 CountDownLatch 用作一个简单的开/关锁存器,或入口:在通过调用 countDown() 的线程打开入口前,所有调用 await 的线程都一直在入口处等待。用 N 初始化的 CountDownLatch 可以使一个线程在 N 个线程完成某项操作之前一直等待,或者使其在某项操作完成 N 次之前一直等待。 CountDownLatch 的一个有用特性是,它不要求调用 countDown 方法的线程等到计数到达零时才继续,而在所有线程都能通过之前,它只是阻止任何线程继续通过一个 await。
源码示例如下:
package multiThreadShow;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
public class MultiThreadShow {
public static void main(String[] args){
HashMap<Integer, ArrayList<Integer>> map = new HashMap<Integer,ArrayList<Integer>>();
HashMap<Integer, ArrayList<Integer>> map2 = new HashMap<Integer,ArrayList<Integer>>();
for(int i=0;i<50;i++){
ArrayList<Integer> arrayList= new ArrayList<Integer>();
for(int i1=0;i1<10000;i1++){
int p = (int) (Math.random()*10000);
arrayList.add(p);
}
map.put(i, arrayList);
}
for(int i=0;i<50;i++){
ArrayList<Integer> arrayList= new ArrayList<Integer>();
for(int i1=0;i1<10000;i1++){
int p = (int) (Math.random()*10000);
arrayList.add(p);
}
map2.put(i, arrayList);
}
CountDownLatch doneSignal = new CountDownLatch(map.size());
long start = System.currentTimeMillis();
for(int i : map.keySet()){
SortThread t= new SortThread(doneSignal,"SortThread-"+i);
t.setArrayList(map.get(i));
t.start();
}
try {
doneSignal.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("Excuting this totally costs "+(end - start));
start = System.currentTimeMillis();
final long input = start;
//其中的Runnable任务用来统计这批任务的执行时间 当所有的任务都达到cyclicBarrier点时(即表示这些任务都完成)
//那么通过这个Runnable任务去统计执行时间
CyclicBarrier cyclicBarrier = new CyclicBarrier(map2.size(),new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("Excuting this totally costs "+(System.currentTimeMillis() - input));
}
});
for(int i : map2.keySet()){
SortThreadCyclicBarrier t= new SortThreadCyclicBarrier(cyclicBarrier,"SortThreadCyclicBarrier-"+i);
t.setArrayList(map2.get(i));
t.start();
}
// end = System.currentTimeMillis();
//main 这个线程执行的顺序可能在阻塞期间 所以这个时间点没有用
//System.out.println("Excuting this totally costs "+(end - start));
}
}
//CountDownLatch排序Thread
class SortThread extends Thread{
private ArrayList<Integer> arrayList;
private final CountDownLatch doneSignal;
public ArrayList<Integer> getArrayList() {
return arrayList;
}
public void setArrayList(ArrayList<Integer> arrayList) {
this.arrayList = arrayList;
}
public SortThread(CountDownLatch doneSignal,String name){
super(name);
this.doneSignal = doneSignal;
}
public void run(){
Collections.sort(arrayList);
doneSignal.countDown();
//System.out.println("Sort finished"+this.getName());
}
}
//CyclicBarrier排序Thread
class SortThreadCyclicBarrier extends Thread{
private ArrayList<Integer> arrayList;
private final CyclicBarrier cyclicBarrier;
public ArrayList<Integer> getArrayList() {
return arrayList;
}
public void setArrayList(ArrayList<Integer> arrayList) {
this.arrayList = arrayList;
}
public SortThreadCyclicBarrier(CyclicBarrier cyclicBarrier,String name){
super(name);
this.cyclicBarrier = cyclicBarrier;
}
public void run(){
System.out.println("Sort Start "+this.getName());
Collections.sort(arrayList);
System.out.println("Sort finished "+this.getName());
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
return;
} catch (BrokenBarrierException e) {
// TODO Auto-generated catch block
return;
}
//System.out.println("Sort finished "+this.getName());
}
}
说明:这两种流程控制手段的区别在于 后者可以循环使用,而且后者控制的线程一个出现问题,则剩下的线程都会以抛出BrokenBarrierException 的方式终止。