1、CountDownLatch
解析:
同步倒数计数器 CountDownLatch初始化一个计数器值,其countDown方法会把计数器值减1, await()方法会阻塞后面程序执行直到计数器值减为0。
Demo:
package com.fedomn.demo; import java.util.concurrent.CountDownLatch; public class Worker { private final static int WorkerNum = 5; public static void main(String args[]){ final CountDownLatch startCountDown = new CountDownLatch(1); final CountDownLatch endCountDown = new CountDownLatch(WorkerNum); final CountDownLatch workerCountDown = new CountDownLatch(WorkerNum); for(int i=0;i<WorkerNum;i++){ new Thread(String.valueOf(i)){ public void run(){ workerCountDown.countDown(); System.out.println("我是工人 "+this.getName()+" 号,已经准备好工作!"); try { startCountDown.await();//等待发出开始工作 指令 } catch (InterruptedException e) { e.printStackTrace(); } try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("我是工人 "+this.getName()+" 号,已经完成工作!"); endCountDown.countDown(); } }.start(); } try { workerCountDown.await(); } catch (InterruptedException e1) { e1.printStackTrace(); } System.out.println("开始工作!"); startCountDown.countDown();//发出开始工作 指令 try { endCountDown.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("工作结束!"); } }
输出结果:
我是工人 0 号,已经准备好工作! 我是工人 3 号,已经准备好工作! 我是工人 1 号,已经准备好工作! 我是工人 2 号,已经准备好工作! 我是工人 4 号,已经准备好工作! 开始工作! 我是工人 0 号,已经完成工作! 我是工人 2 号,已经完成工作! 我是工人 1 号,已经完成工作! 我是工人 3 号,已经完成工作! 我是工人 4 号,已经完成工作! 工作结束!
2、CyclicBarrier
解析:
1、CyclicBarrier初始化时规定一个数目,然后计算调用了CyclicBarrier.await()进入等待的线程数。当等待线程数达到了这个数目时,所有进入等待状态的线程被唤醒并继续
2、CyclicBarrier就象它名字的意思一样,可看成是个障碍, 所有的线程必须到齐后才能一起通过这个障碍
3、CyclicBarrier初始时还可带一个Runnable的参数, 此Runnable任务在CyclicBarrier的数目达到后,所有其它线程被唤醒前被执行
Demo:
package com.fedomn.demo; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; public class CycWroker { private final static int threadNum = 3; private final static CyclicBarrier c = new CyclicBarrier(threadNum,new Runnable() { public void run() { System.out.println("我是Runnable老板!"); } }); public static void main(String args[]){ for(int i=0;i<threadNum;i++){ new Thread(String.valueOf(i)){ public void run() { System.out.println("我是工人 "+this.getName()+" 号,工作第一步"); try { c.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } System.out.println("我是工人 "+this.getName()+" 号,工作 第二步"); try { c.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } } }.start(); } } }
输出结果:
我是工人 0 号,工作第一步 我是工人 2 号,工作第一步 我是工人 1 号,工作第一步 我是Runnable老板! 我是工人 1 号,工作 第二步 我是工人 0 号,工作 第二步 我是工人 2 号,工作 第二步 我是Runnable老板!
3、Semaphor
解析:
设置一个数字,当请求的数量达到设定数字,就将请求拦截下来,直到有线程释放了资源,才会放一个请求进去。
其中acquire 方法获得许可,release 方法释放许可。其他方法参见API
Demo:
package com.fedomn.demo; import java.util.concurrent.Semaphore; public class SemWorker { private final static Semaphore sem = new Semaphore(3); public static void main(String[] args) { for(int i=0;i<10;i++){ new Thread(String.valueOf(i)){ public void run() { try { sem.acquire();//获取一个许可 System.out.println("我是工人 "+this.getName()+" 号,工作Start!"); Thread.sleep(1000); System.out.println("我是工人 "+this.getName()+" 号,工作End!"); } catch (InterruptedException e) { e.printStackTrace(); }finally{ sem.release();//释放一个许可 } } }.start(); } } }
输出结果:
我是工人 0 号,工作Start! 我是工人 5 号,工作Start! 我是工人 1 号,工作Start! 我是工人 5 号,工作End! 我是工人 0 号,工作End! 我是工人 3 号,工作Start! 我是工人 1 号,工作End! 我是工人 4 号,工作Start! 我是工人 2 号,工作Start! 我是工人 2 号,工作End! 我是工人 3 号,工作End! 我是工人 4 号,工作End! 我是工人 6 号,工作Start! 我是工人 7 号,工作Start! 我是工人 8 号,工作Start! 我是工人 6 号,工作End! 我是工人 9 号,工作Start! 我是工人 7 号,工作End! 我是工人 8 号,工作End! 我是工人 9 号,工作End!
4、总结
CountDownLatch:
适用于一组线程和另一个主线程之间的工作协作。一个主线程等待一组工作线程的任务完毕才继续它的执行是使用。
初始化计数器值线程个数,组中每个线程countDown使计数器值到0时,解除await()阻塞,执行主线程后面的程序。
CyclicBarrier:适用于一组或几组线程在一个时间点达到同步,可以一起开始执行下一步任务(如构造函数传入的Runnable)。
初始化等待线程的数目N,组中每个线程await()进入等待,当等待线程为N时,所有等待线程都被唤醒继续执行。
Semaphore:
只允许一定数量的线程同时执行一段任务。
初始化线程允许数量N,每个线程通过acquire()来获得许可,必须等线程release()释放许可,才能接受下一个线程。