一、条件变量
条件变量实现了java.util.concurrent.locks.Condition接口,条件变量的实例化就是通过一个Lock对象上调用newCondition()方法获得的,这样条件就和一个锁绑定起来了。因此,Java中的条件变量只能和锁配合使用,来控制并发程序访问竞争资源的安全。
条件变量的出现是为了更精细的控制线程等待与唤醒,一个锁可以有多个条件,每个条件上有多个线程等待,通过await()方法,可以让线程在该条件下等待。当调用signalAll()方法时,又可以唤醒该条件下等待的线程。条件变量比较抽象,原因是它不是自然语言中的条件概念,而是控制程序的一种手段。
看个例子,有一个账户,多个用户(线程)在同时操作这个账户,有的存款有的取款,存款随便存,但取款有限制,不能透支,任何试图透支的操作都将等待里面有足够的存款时才执行操作。
CaseTest.java
1 package Thread; 2 import java.util.*; 3 import java.util.concurrent.ExecutorService; 4 import java.util.concurrent.Executors; 5 import java.util.concurrent.locks.Condition; 6 import java.util.concurrent.locks.Lock; 7 import java.util.concurrent.locks.ReentrantLock; 8 public class CaseTest { 9 public static void main(String[] args){ 10 MyCount1 myCount1=new MyCount1("6516431",10000); 11 ExecutorService pool=Executors.newFixedThreadPool(2); 12 Thread t1=new SaveThread("张三",myCount1,2000); 13 Thread t2=new DrawThread("李四",myCount1,3600); 14 Thread t3=new SaveThread("王二",myCount1,2700); 15 Thread t4=new SaveThread("麻子",myCount1,600); 16 Thread t5=new DrawThread("胖子",myCount1,1300); 17 Thread t6=new DrawThread("小刘",myCount1,800); 18 pool.execute(t1); 19 pool.execute(t2); 20 pool.execute(t3); 21 pool.execute(t4); 22 pool.execute(t5); 23 pool.execute(t6); 24 pool.shutdown(); 25 } 26 } 27 class SaveThread extends Thread{ 28 private String name; 29 private MyCount1 myCount1; 30 private int x; 31 SaveThread(String name,MyCount1 myCount1,int x){ 32 this.name=name; 33 this.myCount1=myCount1; 34 this.x=x; 35 } 36 public void run(){ 37 myCount1.saving(x,name); 38 } 39 } 40 class DrawThread extends Thread{ 41 private String name; 42 private MyCount1 myCount; 43 private int x; 44 DrawThread(String name,MyCount1 myCount,int x){ 45 this.name=name; 46 this.myCount=myCount; 47 this.x=x; 48 } 49 public void run(){ 50 myCount.drawing(x,name); 51 } 52 } 53 class MyCount1{ 54 private String oid; 55 private int cash; 56 private Lock lock=new ReentrantLock(); 57 private Condition _save=lock.newCondition(); 58 private Condition _draw=lock.newCondition(); 59 MyCount1(String oid,int cash){ 60 this.oid=oid; 61 this.cash=cash; 62 } 63 public void saving(int x,String name){ 64 lock.lock(); //获取锁 65 if(x>0){ 66 cash+=x; 67 System.out.println(name+"存款"+x+",当前余额为:"+cash); 68 } 69 _draw.signalAll(); //唤醒所有等待线程。 70 lock.unlock(); //释放锁 71 } 72 public void drawing(int x,String name){ 73 lock.lock(); 74 try{ 75 if(cash-x<0){ 76 _draw.await();//阻塞取款操作 77 }else{ 78 cash-=x; 79 System.out.println(name+"取款"+x+",当前余额为:"+cash); 80 } 81 _save.signalAll();//唤醒所有存款操作 82 }catch(InterruptedException e){ 83 e.printStackTrace(); 84 }finally{ 85 lock.unlock(); 86 } 87 } 88 }
结果为:
1 李四取款3600,当前余额为:6400 2 张三存款2000,当前余额为:8400 3 王二存款2700,当前余额为:11100 4 麻子存款600,当前余额为:11700 5 胖子取款1300,当前余额为:10400 6 小刘取款800,当前余额为:9600
如果不用条件变量和锁,如何实现此功能呢?
CaseTest.java
1 package Thread; 2 import java.util.*; 3 import java.util.concurrent.ExecutorService; 4 import java.util.concurrent.Executors; 5 import java.util.concurrent.locks.Condition; 6 import java.util.concurrent.locks.Lock; 7 import java.util.concurrent.locks.ReentrantLock; 8 public class CaseTest { 9 public static void main(String[] args){ 10 MyCount1 myCount1=new MyCount1("6516431",10000); 11 ExecutorService pool=Executors.newFixedThreadPool(2); 12 Thread t1=new SaveThread("张三",myCount1,2000); 13 Thread t2=new DrawThread("李四",myCount1,3600); 14 Thread t3=new SaveThread("王二",myCount1,2700); 15 Thread t4=new SaveThread("麻子",myCount1,600); 16 Thread t5=new DrawThread("胖子",myCount1,1300); 17 Thread t6=new DrawThread("小刘",myCount1,800); 18 pool.execute(t1); 19 pool.execute(t2); 20 pool.execute(t3); 21 pool.execute(t4); 22 pool.execute(t5); 23 pool.execute(t6); 24 pool.shutdown(); 25 } 26 } 27 class SaveThread extends Thread{ 28 private String name; 29 private MyCount1 myCount1; 30 private int x; 31 SaveThread(String name,MyCount1 myCount1,int x){ 32 this.name=name; 33 this.myCount1=myCount1; 34 this.x=x; 35 } 36 public void run(){ 37 myCount1.saving(x,name); 38 } 39 } 40 class DrawThread extends Thread{ 41 private String name; 42 private MyCount1 myCount; 43 private int x; 44 DrawThread(String name,MyCount1 myCount,int x){ 45 this.name=name; 46 this.myCount=myCount; 47 this.x=x; 48 } 49 public void run(){ 50 myCount.drawing(x,name); 51 } 52 } 53 class MyCount1{ 54 private String oid; 55 private int cash; 56 MyCount1(String oid,int cash){ 57 this.oid=oid; 58 this.cash=cash; 59 } 60 public synchronized void saving(int x,String name){ 61 if(x>0){ 62 cash+=x; 63 System.out.println(name+"存款"+x+",当前余额为:"+cash); 64 } 65 notifyAll(); 66 //_draw.signalAll(); //唤醒所有等待线程。 67 //lock.unlock(); //释放锁 68 } 69 public synchronized void drawing(int x,String name){ 70 if(cash-x<0){ 71 try{ 72 wait(); 73 } 74 catch(InterruptedException e){ 75 e.printStackTrace(); 76 } 77 }else{ 78 cash-=x; 79 System.out.println(name+"取款"+x+",当前余额为:"+cash); 80 } 81 notifyAll(); 82 83 } 84 }
第二种方式是用同步方法的第一种方法,以前说过这种方法不如第二种好,下面改进一下该同步的程序:
CaseTest.java
1 package Thread; 2 import java.util.*; 3 import java.util.concurrent.ExecutorService; 4 import java.util.concurrent.Executors; 5 import java.util.concurrent.locks.Condition; 6 import java.util.concurrent.locks.Lock; 7 import java.util.concurrent.locks.ReentrantLock; 8 public class CaseTest { 9 public static void main(String[] args){ 10 MyCount1 myCount1=new MyCount1("6516431",10000); 11 ExecutorService pool=Executors.newFixedThreadPool(2); 12 Thread t1=new SaveThread("张三",myCount1,2000); 13 Thread t2=new DrawThread("李四",myCount1,3600); 14 Thread t3=new SaveThread("王二",myCount1,2700); 15 Thread t4=new SaveThread("麻子",myCount1,600); 16 Thread t5=new DrawThread("胖子",myCount1,1300); 17 Thread t6=new DrawThread("小刘",myCount1,800); 18 pool.execute(t1); 19 pool.execute(t2); 20 pool.execute(t3); 21 pool.execute(t4); 22 pool.execute(t5); 23 pool.execute(t6); 24 pool.shutdown(); 25 } 26 } 27 class SaveThread extends Thread{ 28 private String name; 29 private MyCount1 myCount1; 30 private int x; 31 SaveThread(String name,MyCount1 myCount1,int x){ 32 this.name=name; 33 this.myCount1=myCount1; 34 this.x=x; 35 } 36 public void run(){ 37 myCount1.saving(x,name); 38 } 39 } 40 class DrawThread extends Thread{ 41 private String name; 42 private MyCount1 myCount; 43 private int x; 44 DrawThread(String name,MyCount1 myCount,int x){ 45 this.name=name; 46 this.myCount=myCount; 47 this.x=x; 48 } 49 public void run(){ 50 myCount.drawing(x,name); 51 } 52 } 53 class MyCount1{ 54 private String oid; 55 private int cash; 56 //private Lock lock=new ReentrantLock(); 57 //private Condition _save=lock.newCondition(); 58 //private Condition _draw=lock.newCondition(); 59 MyCount1(String oid,int cash){ 60 this.oid=oid; 61 this.cash=cash; 62 } 63 public void saving(int x,String name){ 64 synchronized(this){ 65 if(x>0){ 66 cash+=x; 67 System.out.println(name+"存款"+x+",当前余额为:"+cash); 68 } 69 notifyAll(); 70 } 71 } 72 public void drawing(int x,String name){ 73 synchronized(this){ 74 if(cash-x<0){ 75 try{ 76 wait(); 77 } 78 catch(InterruptedException e){ 79 e.printStackTrace(); 80 } 81 }else{ 82 cash-=x; 83 System.out.println(name+"取款"+x+",当前余额为:"+cash); 84 } 85 notifyAll(); 86 } 87 } 88 }
二、Volatile变量
具体的内容,详见 JAVA理论与实践:正确使用Volatile变量
三、原子量
所谓原子量就是操作变量的操作是“原子的”,该操作不可再分,因此线程是安全的。volatile、synchronized关键字来解决并发访问的安全问题,但这样太麻烦。有一个用来进行单变量多线程并发安全访问的工具包java.util.concurrent.atmoic。
Test.java
1 package Thread; 2 import java.util.concurrent.ExecutorService; 3 import java.util.concurrent.Executors; 4 import java.util.concurrent.atomic.AtomicLong; 5 public class CaseTest { 6 public static void main(String[] args){ 7 ExecutorService pool=Executors.newFixedThreadPool(2); 8 Runnable t1=new MyRunnable_2("张三",2000); 9 Runnable t2=new MyRunnable_2("李四",3600); 10 Runnable t3=new MyRunnable_2("王二",2700); 11 Runnable t4=new MyRunnable_2("麻子",600); 12 Runnable t5=new MyRunnable_2("胖子",1300); 13 Runnable t6=new MyRunnable_2("小刘",800); 14 pool.execute(t1); 15 pool.execute(t2); 16 pool.execute(t3); 17 pool.execute(t4); 18 pool.execute(t5); 19 pool.execute(t6); 20 pool.shutdown(); 21 } 22 } 23 class MyRunnable_2 implements Runnable{ 24 private static AtomicLong aLong = new AtomicLong(10000); 25 private String name; 26 private int x; 27 MyRunnable_2(String name,int x){ 28 this.name=name; 29 this.x=x; 30 } 31 public void run(){ 32 System.out.println(name+"执行了"+x+",余额为:"+aLong.addAndGet(x)); 33 } 34 }
结果为
1 /**第一种结果**/ 2 张三执行了2000,余额为:12000 3 王二执行了2700,余额为:14700 4 麻子执行了600,余额为:15300 5 胖子执行了1300,余额为:16600 6 小刘执行了800,余额为:17400 7 李四执行了3600,余额为:21000 8 9 /**第二种结果**/ 10 张三执行了2000,余额为:15600 11 李四执行了3600,余额为:13600 12 王二执行了2700,余额为:18300 13 麻子执行了600,余额为:18900 14 胖子执行了1300,余额为:20200 15 小刘执行了800,余额为:21000
这个例子是个反例,可见到虽然使用了原子量,但是并发访问还是有问题,那么问题在哪?原子量虽然可以保证单个变量在某一个操作过程安全,但无法保证整个代码块,或者说整个程序的安全。因此,通常可以使用锁等同步机制控制整个程序的安全性。
Test.java
1 package Thread; 2 import java.util.concurrent.ExecutorService; 3 import java.util.concurrent.Executors; 4 import java.util.concurrent.atomic.AtomicLong; 5 import java.util.concurrent.locks.Lock; 6 import java.util.concurrent.locks.ReentrantLock; 7 public class CaseTest { 8 public static void main(String[] args){ 9 ExecutorService pool=Executors.newFixedThreadPool(2); 10 Lock lock=new ReentrantLock(false); 11 Runnable t1=new MyRunnable_2("张三",2000,lock); 12 Runnable t2=new MyRunnable_2("李四",3600,lock); 13 Runnable t3=new MyRunnable_2("王二",2700,lock); 14 Runnable t4=new MyRunnable_2("麻子",600,lock); 15 Runnable t5=new MyRunnable_2("胖子",1300,lock); 16 Runnable t6=new MyRunnable_2("小刘",800,lock); 17 pool.execute(t1); 18 pool.execute(t2); 19 pool.execute(t3); 20 pool.execute(t4); 21 pool.execute(t5); 22 pool.execute(t6); 23 pool.shutdown(); 24 } 25 } 26 class MyRunnable_2 implements Runnable{ 27 private static AtomicLong aLong = new AtomicLong(10000); 28 private String name; 29 private int x; 30 private Lock lock; 31 MyRunnable_2(String name,int x,Lock lock){ 32 this.name=name; 33 this.x=x; 34 this.lock=lock; 35 } 36 public void run(){ 37 lock.lock(); 38 System.out.println(name+"执行了"+x+",余额为:"+aLong.addAndGet(x)); 39 lock.unlock(); 40 } 41 }
结果为:
1 张三执行了2000,余额为:12000 2 李四执行了3600,余额为:15600 3 王二执行了2700,余额为:18300 4 胖子执行了1300,余额为:19600 5 麻子执行了600,余额为:20200 6 小刘执行了800,余额为:21000
此时,加入了一个对象锁,来控制并发访问的控制,不管程序运行多少次,结果都是一样的。有关原子的用法仅仅保证变量操作的原子性,但是需要考虑整个过程的线程安全性。
四、信号量
一个信号量管理很多的许可证,为了获取信号量,线程通过调用acquire请求许可。Java信号量实际上是一个功能完毕的计数器,并由此限制了通过的线程数量,其他线程可以通过调用release释放许可。
它对控制一定资源的消费与回收有着重要意义,信号量常常用于多线程的代码中,并能监控有多少数目的线程等待获取资源,并且通过信号量可以得知可用资源的数目等等,这里强调数目二字,并不是指有哪些在等待,哪些资源可用。例子:
SignalTest.java
1 package Thread; 2 3 import java.util.concurrent.ExecutorService; 4 import java.util.concurrent.Executors; 5 import java.util.concurrent.Semaphore; 6 7 public class SignalTest { 8 public static void main(String[] args){ 9 MyPool myPool=new MyPool(20); 10 ExecutorService threadPool=Executors.newFixedThreadPool(2); 11 MyThread_signal t1=new MyThread_signal("任务A",myPool,3); 12 MyThread_signal t2=new MyThread_signal("任务B",myPool,12); 13 MyThread_signal t3=new MyThread_signal("任务C",myPool,7); 14 threadPool.execute(t1); 15 threadPool.execute(t2); 16 threadPool.execute(t3); 17 threadPool.shutdown(); 18 } 19 } 20 class MyPool{ 21 private Semaphore sp;//池相关的信号量 22 MyPool(int size){this.sp=new Semaphore(size);} 23 public Semaphore getSp(){return sp;} 24 public void setSp(Semaphore sp){this.sp=sp;} 25 } 26 class MyThread_signal extends Thread{ 27 private String name; 28 private MyPool myPool; 29 private int x; 30 MyThread_signal(String name,MyPool myPool,int x){ 31 this.name=name; 32 this.myPool=myPool; 33 this.x=x; 34 } 35 public void run(){ 36 try{ 37 myPool.getSp().acquire(); 38 System.out.println(name+"成功获取了"+x+"个许可!"); 39 } 40 catch(InterruptedException e){e.printStackTrace();} 41 finally{myPool.getSp().release(x); 42 System.out.println(name+"释放了"+x+"个许可!");} 43 } 44 }
结果为:
1 任务A成功获取了3个许可! 2 任务A释放了3个许可! 3 任务C成功获取了7个许可! 4 任务C释放了7个许可! 5 任务B成功获取了12个许可! 6 任务B释放了12个许可!
信号量仅仅是对池资源进行监控,但不能 保证线程的安全,因此,应该自己控制线程的安全访问资源。
五、线程池
线程池的思想还是一种对象池的思想,开辟一块内存空间,里面存放众多的(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完线程对象归池,这样可以避免反复创建线程对象带来的性能开销,节约系统资源。
线程池分为固定尺寸的线程池、可变尺寸线程池。
1、固定大小的线程池
1 package Thread; 2 3 import java.util.concurrent.ExecutorService; 4 import java.util.concurrent.Executors; 5 6 public class Test1 { 7 public static void main(String[] args){ 8 ExecutorService pool=Executors.newFixedThreadPool(2); 9 Thread t1=new MyThread_test(); 10 Thread t2=new MyThread_test(); 11 Thread t3=new MyThread_test(); 12 Thread t4=new MyThread_test(); 13 pool.execute(t1); 14 pool.execute(t2); 15 pool.execute(t3); 16 pool.execute(t4); 17 pool.shutdown(); 18 } 19 } 20 class MyThread_test extends Thread{ 21 public void run(){ 22 System.out.println(Thread.currentThread().getName()+"正在执行。。"); 23 } 24 }
1 pool-1-thread-1正在执行。。 2 pool-1-thread-2正在执行。。 3 pool-1-thread-1正在执行。。 4 pool-1-thread-2正在执行。。
2、单任务线程池
在上一例修改一行pool对象的代码为:
1 ExecutorService pool=Executors.newSingleThreadExecutor();
1 pool-1-thread-1正在执行。。 2 pool-1-thread-1正在执行。。 3 pool-1-thread-1正在执行。。 4 pool-1-thread-1正在执行。。
以上两种情况都是大小固定的,当要加入的池的线程(或任务)超过池最大尺寸的时候,则入此线程池需要排队等待。
3、可变尺寸的线程池
1 ExecutorService pool=Executors.newCachedThreadPool();
pool-1-thread-1正在执行。。 pool-1-thread-4正在执行。。 pool-1-thread-3正在执行。。 pool-1-thread-2正在执行。。
4、延迟线程池
1 package Thread; 2 3 import java.util.concurrent.ExecutorService; 4 import java.util.concurrent.Executors; 5 import java.util.concurrent.ScheduledExecutorService; 6 import java.util.concurrent.TimeUnit; 7 8 public class Test1 { 9 public static void main(String[] args){ 10 //ExecutorService pool=Executors.newFixedThreadPool(2); 11 //ExecutorService pool=Executors.newSingleThreadExecutor(); 12 //ExecutorService pool=Executors.newCachedThreadPool(); 13 ScheduledExecutorService pool=Executors.newScheduledThreadPool(2); 14 Thread t1=new MyThread_test(); 15 Thread t2=new MyThread_test(); 16 Thread t3=new MyThread_test(); 17 Thread t4=new MyThread_test(); 18 Thread t5=new MyThread_test(); 19 Thread t6=new MyThread_test(); 20 pool.execute(t1); 21 pool.execute(t2); 22 pool.execute(t3); 23 pool.execute(t4); 24 pool.schedule(t5,10,TimeUnit.MILLISECONDS); 25 pool.schedule(t6,10,TimeUnit.MILLISECONDS); 26 pool.shutdown(); 27 } 28 } 29 class MyThread_test extends Thread{ 30 public void run(){ 31 System.out.println(Thread.currentThread().getName()+"正在执行。。"); 32 } 33 }
1 pool-1-thread-1正在执行。。 2 pool-1-thread-2正在执行。。 3 pool-1-thread-1正在执行。。 4 pool-1-thread-1正在执行。。 5 pool-1-thread-2正在执行。。 6 pool-1-thread-1正在执行。。
5、单任务延迟线程池
在4的代码基础上修改为:
1 ScheduledExecutorService pool=Executors.newSingleThreadScheduledExecutor();
1 pool-1-thread-1正在执行。。 2 pool-1-thread-1正在执行。。 3 pool-1-thread-1正在执行。。 4 pool-1-thread-1正在执行。。 5 pool-1-thread-1正在执行。。 6 pool-1-thread-1正在执行。。
6、自定义线程池
1 package Thread; 2 3 import java.util.concurrent.ArrayBlockingQueue; 4 import java.util.concurrent.BlockingQueue; 5 import java.util.concurrent.ExecutorService; 6 import java.util.concurrent.Executors; 7 import java.util.concurrent.ScheduledExecutorService; 8 import java.util.concurrent.ThreadPoolExecutor; 9 import java.util.concurrent.TimeUnit; 10 11 public class Test1 { 12 public static void main(String[] args){ 13 //ExecutorService pool=Executors.newFixedThreadPool(2); 14 //ExecutorService pool=Executors.newSingleThreadExecutor(); 15 //ExecutorService pool=Executors.newCachedThreadPool(); 16 //ScheduledExecutorService pool=Executors.newScheduledThreadPool(2); 17 //ScheduledExecutorService pool=Executors.newSingleThreadScheduledExecutor(); 18 BlockingQueue<Runnable>bqueue=new ArrayBlockingQueue<Runnable>(20); 19 ThreadPoolExecutor pool=new ThreadPoolExecutor(2,3,2,TimeUnit.MILLISECONDS,bqueue); 20 21 Thread t1=new MyThread_test(); 22 Thread t2=new MyThread_test(); 23 Thread t3=new MyThread_test(); 24 Thread t4=new MyThread_test(); 25 Thread t5=new MyThread_test(); 26 Thread t6=new MyThread_test(); 27 pool.execute(t1); 28 pool.execute(t2); 29 pool.execute(t3); 30 pool.execute(t4); 31 pool.execute(t5); 32 pool.execute(t6); 33 // pool.schedule(t5,10,TimeUnit.MILLISECONDS); 34 //pool.schedule(t6,10,TimeUnit.MILLISECONDS); 35 pool.shutdown(); 36 } 37 } 38 class MyThread_test extends Thread{ 39 public void run(){ 40 System.out.println(Thread.currentThread().getName()+"正在执行。。"); 41 } 42 }
1 pool-1-thread-1正在执行。。 2 pool-1-thread-2正在执行。。 3 pool-1-thread-1正在执行。。 4 pool-1-thread-2正在执行。。 5 pool-1-thread-1正在执行。。 6 pool-1-thread-2正在执行。。
创建自定义线程池的构造方法很多,本例中的含义如下:
1 public tThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable>workQueue) 2 3 参数 4 corePoolSize:池中所保存的线程数,包括空闲线程。 5 maximumPoolSize:池中允许的最大线程数。 6 keepAliveTime:当线程大于核心时,此为终止前多余的空前线程等待新任务的最长时间。 7 unit-keepAliveTime:参数的时间单位 8 workQueue:执行前用于保持任务队列,此队列仅保持有executor方法提交的Runnable任务。 9 10 抛出: 11 IllegalArgumentException:如果corePoolSize或keepAliveTime小于0或者maximumPoolSize小于等于0,或者说corePoolSize大于maximumPoolSize。 12 NullPointerException:如果workQueue为空
虽然自定义线程池麻烦点,但是可以获取当前线程池的尺寸、正在执行任务的线程数、工作队列等。
六、障碍器
当计算一个大的任务时,常常需要分配好多子任务去执行,只有当所有子任务执行完时,才能执行主任务,这时候需要借助障碍器。
1 package Thread; 2 3 import java.util.concurrent.BrokenBarrierException; 4 import java.util.concurrent.CyclicBarrier; 5 6 public class CarrierTest { 7 public static void main(String[] args){ 8 CyclicBarrier cb=new CyclicBarrier(6,new MainTask()); 9 new SubTask("A",cb).start(); 10 new SubTask("B",cb).start(); 11 new SubTask("C",cb).start(); 12 new SubTask("D",cb).start(); 13 new SubTask("E",cb).start(); 14 new SubTask("F",cb).start(); 15 } 16 } 17 class MainTask implements Runnable{ 18 public void run(){ 19 System.out.println(">>>>主任务执行了!<<<<"); 20 } 21 } 22 class SubTask extends Thread{ 23 private String name; 24 private CyclicBarrier cb; 25 SubTask(String name,CyclicBarrier cb){ 26 this.name=name; 27 this.cb=cb; 28 } 29 public void run(){ 30 System.out.println("[子任务"+name+"]开始执行了!"); 31 //for(int i=0;i<99999;i++);//模拟耗时的任务 32 System.out.println("[子任务"+name+"]开始执行完成了,并通知障碍器已经完成!"); 33 34 try{ 35 cb.await();//通知障碍器已经完成 36 } 37 catch(InterruptedException e){ 38 e.printStackTrace(); 39 } 40 catch(BrokenBarrierException e){ 41 e.printStackTrace(); 42 } 43 } 44 }
1 [子任务C]开始执行了! 2 [子任务D]开始执行了! 3 [子任务E]开始执行了! 4 [子任务B]开始执行了! 5 [子任务A]开始执行了! 6 [子任务B]开始执行完成了,并通知障碍器已经完成! 7 [子任务E]开始执行完成了,并通知障碍器已经完成! 8 [子任务D]开始执行完成了,并通知障碍器已经完成! 9 [子任务F]开始执行了! 10 [子任务C]开始执行完成了,并通知障碍器已经完成! 11 [子任务F]开始执行完成了,并通知障碍器已经完成! 12 [子任务A]开始执行完成了,并通知障碍器已经完成! 13 >>>>主任务执行了!<<<<