本文主要是参考官方文档做一学习用途。
官方链接:
http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/CountDownLatch.html
http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/CyclicBarrier.html
多线程设计过程中,经常会遇到需要等待其它线程结束以后再做其他事情的情况,比如多线程下载文件,每个线程都会下载文件的一部分,在所有线程结束以后,需要将各部分再次拼接成一个完整的文件。
有几种方案:
1.在主线程中设置一自定义全局计数标志,在工作线程完成时,计数减一。主线程侦测该标志是否为0,一旦为0,表示所有工作线程已经完成。
2.使用Java标准的类CountDownLatch来完成这项工作,原理是一样的,计数。
CountDownLatch
CountDownLatch 初始化设置count,即等待(await)count个线程或一个线程count次计数,通过工作线程来countDown计数减一,直到计数为0,await阻塞结束。
设置的count不可更改,如需要动态设置计数的线程数,可以使用CyclicBarrier.
下面的例子,所有的工作线程中准备就绪以后,并不是直接运行,而是等待主线程的信号后再执行具体的操作。
packagecom.example.multithread;
importjava.util.concurrent.CountDownLatch;
classDriver
{
privatestaticfinalintTOTAL_THREADS =10;
privatefinalCountDownLatch mStartSignal =newCountDownLatch(1);
privatefinalCountDownLatch mDoneSignal =newCountDownLatch(TOTAL_THREADS);
voidmain()
{
for(inti =0; i
{
newThread(newWorker(mStartSignal, mDoneSignal, i)).start();
}
System.out.println("Main Thread Now:"+ System.currentTimeMillis());
doPrepareWork();// 准备工作
mStartSignal.countDown();// 计数减一为0,工作线程真正启动具体操作
doSomethingElse();//做点自己的事情
try
{
mDoneSignal.await();// 等待所有工作线程结束
}
catch(InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("All workers have finished now.");
System.out.println("Main Thread Now:"+ System.currentTimeMillis());
}
voiddoPrepareWork()
{
System.out.println("Ready,GO!");
}
voiddoSomethingElse()
{
for(inti =0; i <100000; i++)
{
;// delay
}
System.out.println("Main Thread Do something else.");
}
}
classWorkerimplementsRunnable
{
privatefinalCountDownLatch mStartSignal;
privatefinalCountDownLatch mDoneSignal;
privatefinalintmThreadIndex;
Worker(finalCountDownLatch startSignal,finalCountDownLatch doneSignal,
finalintthreadIndex)
{
this.mDoneSignal = doneSignal;
this.mStartSignal = startSignal;
this.mThreadIndex = threadIndex;
}
@Override
publicvoidrun()
{
// TODO Auto-generated method stub
try
{
mStartSignal.await();// 阻塞,等待mStartSignal计数为0运行后面的代码
// 所有的工作线程都在等待同一个启动的命令
doWork();// 具体操作
System.out.println("Thread "+ mThreadIndex +" Done Now:"
+ System.currentTimeMillis());
mDoneSignal.countDown();// 完成以后计数减一
}
catch(InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
publicvoiddoWork()
{
for(inti =0; i <1000000; i++)
{
;// 耗时操作
}
System.out.println("Thread "+ mThreadIndex +":do work");
}
}
publicclassCountDownLatchTest
{
publicstaticvoidmain(String[] args)
{
// TODO Auto-generated method stub
newDriver().main();
}
}
通过Executor启动线程:
classCountDownLatchDriver2
{
privatestaticfinalintTOTAL_THREADS =10;
privatefinalCountDownLatch mDoneSignal =newCountDownLatch(TOTAL_THREADS);
voidmain()
{
System.out.println("Main Thread Now:"+ System.currentTimeMillis());
doPrepareWork();// 准备工作
Executor executor = Executors.newFixedThreadPool(TOTAL_THREADS);
for(inti =0; i
{
// 通过内建的线程池维护创建的线程
executor.execute(newRunnableWorker(mDoneSignal, i));
}
doSomethingElse();// 做点自己的事情
try
{
mDoneSignal.await();// 等待所有工作线程结束
}
catch(InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("All workers have finished now.");
System.out.println("Main Thread Now:"+ System.currentTimeMillis());
}
voiddoPrepareWork()
{
System.out.println("Ready,GO!");
}
voiddoSomethingElse()
{
for(inti =0; i <100000; i++)
{
;// delay
}
System.out.println("Main Thread Do something else.");
}
}
classRunnableWorkerimplementsRunnable
{
privatefinalCountDownLatch mDoneSignal;
privatefinalintmThreadIndex;
RunnableWorker(finalCountDownLatch doneSignal,finalintthreadIndex)
{
this.mDoneSignal = doneSignal;
this.mThreadIndex = threadIndex;
}
@Override
publicvoidrun()
{
// TODO Auto-generated method stub
doWork();// 具体操作
System.out.println("Thread "+ mThreadIndex +" Done Now:"
+ System.currentTimeMillis());
mDoneSignal.countDown();// 完成以后计数减一
// 计数为0时,主线程接触阻塞,继续执行其他任务
try
{
// 可以继续做点其他的事情,与主线程无关了
Thread.sleep(5000);
System.out.println("Thread "+ mThreadIndex
+" Do something else after notifing main thread");
}
catch(InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
publicvoiddoWork()
{
for(inti =0; i <1000000; i++)
{
;// 耗时操作
}
System.out.println("Thread "+ mThreadIndex +":do work");
}
}
输出:
Main Thread Now:1359959480786
Ready,GO!
Thread 0:do work
Thread 0 Done Now:1359959480808
Thread 1:do work
Thread 1 Done Now:1359959480811
Thread 2:do work
Thread 2 Done Now:1359959480813
Main Thread Do something else.
Thread 3:do work
Thread 3 Done Now:1359959480825
Thread 5:do work
Thread 5 Done Now:1359959480827
Thread 7:do work
Thread 7 Done Now:1359959480829
Thread 9:do work
Thread 9 Done Now:1359959480831
Thread 4:do work
Thread 4 Done Now:1359959480833
Thread 6:do work
Thread 6 Done Now:1359959480835
Thread 8:do work
Thread 8 Done Now:1359959480837
All workers have finished now.
Main Thread Now:1359959480838
Thread 0 Do something else after notifing main thread
Thread 1 Do something else after notifing main thread
Thread 2 Do something else after notifing main thread
Thread 3 Do something else after notifing main thread
Thread 9 Do something else after notifing main thread
Thread 7 Do something else after notifing main thread
Thread 5 Do something else after notifing main thread
Thread 4 Do something else after notifing main thread
Thread 6 Do something else after notifing main thread
Thread 8 Do something else after notifing main thread
CyclicBarrier
使用CyclickBarrier的例子:
classWalkTarget
{
privatefinalintmCount =5;
privatefinalCyclicBarrier mBarrier;
ExecutorService mExecutor;
classBarrierActionimplementsRunnable
{
@Override
publicvoidrun()
{
// TODO Auto-generated method stub
System.out.println("所有线程都已经完成任务,计数达到预设值");
//mBarrier.reset();//恢复到初始化状态
}
}
WalkTarget()
{
//初始化CyclicBarrier
mBarrier =newCyclicBarrier(mCount,newBarrierAction());
mExecutor = Executors.newFixedThreadPool(mCount);
for(inti =0; i
{
//启动工作线程
mExecutor.execute(newWalker(mBarrier, i));
}
}
}
//工作线程
classWalkerimplementsRunnable
{
privatefinalCyclicBarrier mBarrier;
privatefinalintmThreadIndex;
Walker(finalCyclicBarrier barrier,finalintthreadIndex)
{
mBarrier = barrier;
mThreadIndex = threadIndex;
}
@Override
publicvoidrun()
{
// TODO Auto-generated method stub
System.out.println("Thread "+ mThreadIndex +" is running...");
// 执行任务
try
{
TimeUnit.MILLISECONDS.sleep(5000);
// do task
}
catch(InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
// 完成任务以后,等待其他线程完成任务
try
{
mBarrier.await();
}
catch(InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch(BrokenBarrierException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
// 其他线程任务都完成以后,阻塞解除,可以继续接下来的任务
System.out.println("Thread "+ mThreadIndex +" do something else");
}
}
publicclassCountDownLatchTest
{
publicstaticvoidmain(String[] args)
{
// TODO Auto-generated method stub
//new CountDownLatchDriver2().main();
newWalkTarget();
}
}
输出(注意,只有所有的线程barrier.await之后才能继续执行其他的操作):
Thread 0 is running... Thread 2 is running... Thread 3 is running... Thread 1 is running... Thread 4 is running... 所有线程都已经完成任务,计数达到预设值 Thread 4 do something else Thread 0 do something else Thread 2 do something else Thread 3 do something else Thread 1 do something else
CountDownLatch和CyclicBarrier简单比较:
CountDownLatchCyclicBarrier
软件包
java.util.concurrent
java.util.concurrent
适用情景
主线程等待多个工作线程结束
多个线程之间互相等待,直到所有线程达到一个障碍点(Barrier point)
主要方法
CountDownLatch(int count) (主线程调用)
初始化计数
CountDownLatch.await (主线程调用)
阻塞,直到等待计数为0解除阻塞
CountDownLatch.countDown
计数减一(工作线程调用)
CyclicBarrier(int parties, Runnable barrierAction) //初始化参与者数量和障碍点执行Action,Action可选。由主线程初始化
CyclicBarrier.await() //由参与者调用
阻塞,直到所有线程达到屏障点
等待结束
各线程之间不再互相影响,可以继续做自己的事情。不再执行下一个目标工作。
在屏障点达到后,允许所有线程继续执行,达到下一个目标。可以重复使用CyclicBarrier
异常
如果其中一个线程由于中断,错误,或超时导致永久离开屏障点,其他线程也将抛出异常。
其他
如果BarrierAction不依赖于任何Party中的所有线程,那么在任何party中的一个线程被释放的时候,可以直接运行这个Action。
If(barrier.await()==2)
{
//do action
}