java.uril.concurrent.CyclicBarrier是一个同步辅助类,不过这里所指的同步不是本章前面一直强调的“线程同步”,而是指隶属于同一个线程组中的线程必须在指定的“集合点”上到齐。这种同步就类似于士兵野外拉练,教官要求必须在几个地点集合,再继续分头行动,然后再在某个地点集合....这几个地点就称为“集合点”
CyclicBarrier 类的构造方法要求传入要求同步的线程数量。CyclicBarrier 类最重要的方法是await()方法。正是依靠这个方法实现线程的等待。
通过前面几章内容已经知道,java.util.concurrent.Executor接口及其子接口的实现类代表的就是线程池。java.util.concurrent.ExecutorService 接口继承自java.util.concurrent.Executor接口,除了可以使用Executor接口的execute()方法向线程池提交立即执行的任务,本文还将接触ExecutorService接口自身定义的一个方法submit(Runnable),用于线程池提交任务。
下面代码演示如何辅助线程池中的3个线程在3个“集合点”实现同步:第一号线程休眠时间最短,因此总是第一个到达集合点,它需要等待其他两个线程。
代码【TestCyclicBarrier】
/** * TestCyclicBarrier.java * 版权所有(C) 2011 [email protected] * 创建:崔冉 2011-1-14 下午02:15:11 */ package com.cayden.thread832; import java.util.Date; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @author 崔冉 * @version 1.0.0 * @desc 线程同步辅助类学习 */ public class TestCyclicBarrier { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub //创建需要同步3个线程的同步辅助对象 CyclicBarrier barrier=new CyclicBarrier(3); //创建容量为3的固定线程池,wait ExecutorService exec=Executors.newFixedThreadPool(3); //向线程池提交三个任务 exec.submit(new Runner(barrier,0,new int[]{4,8,12})); exec.submit(new Runner(barrier,1,new int[]{1,2,3})); exec.submit(new Runner(barrier,2,new int[]{2,4,6})); //销毁线程池 exec.shutdown(); } } class Runner implements Runnable { private int no=0;//线程序号 private int [] times=null;//到达每个集合点需要的时间,单位为秒 private CyclicBarrier barrier=null;//同步辅助对象 public Runner(CyclicBarrier barrier,int no,int [] times){ this.times=times; this.no=no; this.barrier=barrier; } public void run(){ try{ Thread.sleep(times[0]*1000); System.out.println(new Date()+"Thread_"+no+" Arrives At Point 1"); barrier.await(); Thread.sleep(times[1]*1000); System.out.println(new Date()+"Thread_"+no+" Arrives At Point 2"); barrier.await(); Thread.sleep(times[2]*1000); System.out.println(new Date()+"Thread_"+no+" Arrives At Point 3"); barrier.await(); } catch (InterruptedException bke) { // TODO: handle exception bke.printStackTrace(); } catch (BrokenBarrierException bke) { // TODO: handle exception bke.printStackTrace(); } } }
运行结果:
Thu Jan 20 09:04:40 CST 2011Thread_1 Arrives At Point 1 Thu Jan 20 09:04:41 CST 2011Thread_2 Arrives At Point 1 Thu Jan 20 09:04:43 CST 2011Thread_0 Arrives At Point 1 Thu Jan 20 09:04:45 CST 2011Thread_1 Arrives At Point 2 Thu Jan 20 09:04:47 CST 2011Thread_2 Arrives At Point 2 Thu Jan 20 09:04:51 CST 2011Thread_0 Arrives At Point 2 Thu Jan 20 09:04:54 CST 2011Thread_1 Arrives At Point 3 Thu Jan 20 09:04:57 CST 2011Thread_2 Arrives At Point 3 Thu Jan 20 09:05:03 CST 2011Thread_0 Arrives At Point 3
java.util.concurrent.CountDownLatch是另一个线程同步辅助类,其作用与CyclicBarrier基本相同,也是锁定自身直到其他线程到达“集合点”才向下继续执行。否则线程便在集合点等待其他线程。但也有不同,两者都认为线程全部到齐"集合点"的依据不同。 CyclicBarrier依据到达”集合点“的线程数是否达到需要同步的线程数来认定是否需要解除锁定。而CountDownLatch依据计数器是否为0来认定是否需要解除锁定。
下面给出代码【TestCountDownLatch】
/** * TestCountDownLatch.java * 版权所有(C) 2011 [email protected] * 创建:崔冉 2011-1-14 下午02:34:54 */ package com.cayden.thread832; import java.util.Date; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @author 崔冉 * @version 1.0.0 * @desc */ public class TestCountDownLatch { /** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { // TODO Auto-generated method stub //计数值为1的同步辅助对象 final CountDownLatch begin=new CountDownLatch(1); //计数值为10的同步辅助对象 final CountDownLatch end=new CountDownLatch(10); //容量为10的线程池 ExecutorService exec=Executors.newFixedThreadPool(10); for(int i=0;i<10;i++){ Runnable run=new Runnable(){ public void run(){ try{ begin.await(); System.out.println(new Date()+" "+Thread.currentThread().getName()+" 开始运行"); Thread.sleep((long)(Math.random()*10000)); System.out.println(new Date()+" "+Thread.currentThread().getName()+" 运行完毕"); }catch (InterruptedException e) { // TODO: handle exception }finally{ end.countDown(); } } }; exec.submit(run); } begin.countDown(); end.await(); System.out.println(new Date()+" "+Thread.currentThread().getName()+" 运行完毕"); exec.shutdown(); } }
运行结果:
Thu Jan 20 09:48:45 CST 2011 pool-1-thread-10 开始运行 Thu Jan 20 09:48:45 CST 2011 pool-1-thread-9 开始运行 Thu Jan 20 09:48:45 CST 2011 pool-1-thread-1 开始运行 Thu Jan 20 09:48:45 CST 2011 pool-1-thread-7 开始运行 Thu Jan 20 09:48:45 CST 2011 pool-1-thread-5 开始运行 Thu Jan 20 09:48:45 CST 2011 pool-1-thread-6 开始运行 Thu Jan 20 09:48:45 CST 2011 pool-1-thread-8 开始运行 Thu Jan 20 09:48:45 CST 2011 pool-1-thread-4 开始运行 Thu Jan 20 09:48:45 CST 2011 pool-1-thread-3 开始运行 Thu Jan 20 09:48:45 CST 2011 pool-1-thread-2 开始运行 Thu Jan 20 09:48:49 CST 2011 pool-1-thread-1 运行完毕 Thu Jan 20 09:48:49 CST 2011 pool-1-thread-4 运行完毕 Thu Jan 20 09:48:50 CST 2011 pool-1-thread-5 运行完毕 Thu Jan 20 09:48:50 CST 2011 pool-1-thread-3 运行完毕 Thu Jan 20 09:48:50 CST 2011 pool-1-thread-2 运行完毕 Thu Jan 20 09:48:51 CST 2011 pool-1-thread-7 运行完毕 Thu Jan 20 09:48:52 CST 2011 pool-1-thread-10 运行完毕 Thu Jan 20 09:48:54 CST 2011 pool-1-thread-8 运行完毕 Thu Jan 20 09:48:55 CST 2011 pool-1-thread-6 运行完毕 Thu Jan 20 09:48:55 CST 2011 pool-1-thread-9 运行完毕 Thu Jan 20 09:48:55 CST 2011 main 运行完毕