CountDownLatch
CountDownLatch是当
所有的线程执行完毕,也就是run方法体执行完了
。再来执行另外一个线程。它是以计数器递减的方式来确定是否
所有的线程都执行完成了。所有的线程执行完成之后,计数器为0。
例子一
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| package com.kongzhong.gw2.ccc.service; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.CountDownLatch; /** * CountDownLatch用法 * 等待所有的线程完成之后,再来完成另外一个线程. * 张三和李四两个人同时开始工作。当两个人都完成了工作之后打印出一句话:“所有工作都完成了在sdf.format(new Date())” * @author Mobim-Client * */ public class TestCountDownLatch { final static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static void main(String[] args) throws InterruptedException { CountDownLatch latch=new CountDownLatch(2);//两个工人的协作 Worker worker1=new Worker("张三", 5000, latch); Worker worker2=new Worker("李四", 8000, latch); worker1.start(); worker2.start(); latch.await();//等待所有工人完成工作 System.out.println("所有工作都完成在 "+sdf.format(new Date())); } static class Worker extends Thread{ String workerName; int workTime; CountDownLatch latch; public Worker(String workerName ,int workTime ,CountDownLatch latch){ this.workerName=workerName; this.workTime=workTime; this.latch=latch; } public void run(){ System.out.println("工人: "+workerName+" 开始工作在 "+sdf.format(new Date())); doWork();//在工作中.... System.out.println("工人: "+workerName+" 工作完成在 "+sdf.format(new Date())); latch.countDown();//工人完成工作,计数器减一 } private void doWork(){ try { Thread.sleep(workTime); } catch (InterruptedException e) { e.printStackTrace(); } } } }
|
执行结果为:
1 2 3 4 5
| 工人: 张三 开始工作在 2016-05-21 14:20:02 工人: 李四 开始工作在 2016-05-21 14:20:02 工人: 张三 工作完成在 2016-05-21 14:20:07 工人: 李四 工作完成在 2016-05-21 14:20:10 所有工作都完成在 2016-05-21 14:20:10
|
例子二
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| package com.kongzhong.gw2.ccc.service;
import java.util.concurrent.CountDownLatch; /** * 共10个线程,每一个线程打印出1,2。当10个线程完成后,再打印出"OK" * @author Mobim-Client * */ public class Test { public static void main(String[] args) throws InterruptedException { CountDownLatch latch=new CountDownLatch(10); for(int i=1;i<=10;i++){ new Print(latch).start(); } latch.await();//等待所有的线程执行完成 System.out.println("OK"); } } class Print extends Thread{ CountDownLatch latch; public Print(CountDownLatch latch) { this.latch=latch; } @Override public void run() { System.out.println(Thread.currentThread().getName()+"---1"); System.out.println(Thread.currentThread().getName()+"---2"); latch.countDown();//计数器减一 } }
|
执行结果为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| Thread-0---1 Thread-2---1 Thread-1---1 Thread-3---1 Thread-2---2 Thread-4---1 Thread-0---2 Thread-4---2 Thread-3---2 Thread-7---1 Thread-7---2 Thread-1---2 Thread-6---1 Thread-6---2 Thread-5---1 Thread-5---2 Thread-9---1 Thread-8---1 Thread-8---2 Thread-9---2 OK
|
CyclicBarrier
CyclicBarrier是所有的线程在run方法中的await中阻塞了。当所有的线程都执行到await时才解除阻塞,继续执行run方法体中未执行的部分
CyclicBarrier是以计数的方式来确定是否都执行到了await,每一个线程执行到了await时计数器会加1。一直加到构造方法中给定的值。然后
就解除阻塞。计算器又回到0,所有CyclicBarrier是可以重用的。
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| /** * 赛跑时,等待所有人都准备好时,才起跑: * @author Mobim-Client * */ public class Test { public static void main(String[] args) throws InterruptedException { CyclicBarrier barrier = new CyclicBarrier(3); //如果把3改成4,则该程序就一直等下去。 new Runner(barrier, "刘翔").start(); new Runner(barrier, "姚明").start(); new Runner(barrier, "潘晓婷").start(); } } class Runner extends Thread{ CyclicBarrier barrier; String name; public Runner(CyclicBarrier barrier,String name) { this.barrier=barrier; this.name=name; } @Override public void run() { try { Thread.sleep(1000 * (new Random()).nextInt(8)); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("选手:"+name+" 准备好了"); try { barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } System.out.println(name + " 起跑!"); } }
|
运行结果:
1 2 3 4 5 6
| 选手:潘晓婷 准备好了 选手:刘翔 准备好了 选手:姚明 准备好了 姚明 起跑! 潘晓婷 起跑! 刘翔 起跑!
|
CountDownLatch与CyclicBarrier的区别
CountDownLatch是减计数方式,计数==0时释放所有等待的线程;CyclicBarrier是加计数方式,计数达到构造方法中参数指定的值时释放所有等待的线程。
CountDownLatch当计数到0时,计数无法被重置;CyclicBarrier计数达到指定值时,计数置为0重新开始(可以继续重用)。
CountDownLatch每次调用countDown()方法计数减一,调用await()方法只进行阻塞,对计数没任何影响;CyclicBarrier只有一个await()方法,调用await()方法计数加1,若加1后的值不等于构造方法的值,则线程阻塞。
CountDownLatch是让每一个线程都执行完成(run方法执行结束),在执行其他的任务
。
CyclicBarrier是每一个线程执行run方法中执行到一部分就阻塞了。等所有的线程都执行到这里之后就解除阻塞,继续执行run方法
CyclicBarrier重用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| public class Test { public static void main(String[] args) { int N = 4; CyclicBarrier barrier = new CyclicBarrier(N); for(int i=0;i<N;i++) { new Writer(barrier).start(); } try { Thread.sleep(25000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("CyclicBarrier重用"); for(int i=0;i<N;i++) { new Writer(barrier).start(); } } static class Writer extends Thread{ private CyclicBarrier cyclicBarrier; public Writer(CyclicBarrier cyclicBarrier) { this.cyclicBarrier = cyclicBarrier; } @Override public void run() { System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据..."); try { Thread.sleep(5000); //以睡眠来模拟写入数据操作 System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕"); cyclicBarrier.await(); //所有的线程在这里等待了。也就是每一个线程只执行了一部分。 } catch (InterruptedException e) { e.printStackTrace(); }catch(BrokenBarrierException e){ e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"所有线程写入完毕,继续处理其他任务..."); } } }
|
执行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| 线程Thread-0正在写入数据... 线程Thread-1正在写入数据... 线程Thread-3正在写入数据... 线程Thread-2正在写入数据... 线程Thread-1写入数据完毕,等待其他线程写入完毕 线程Thread-3写入数据完毕,等待其他线程写入完毕 线程Thread-2写入数据完毕,等待其他线程写入完毕 线程Thread-0写入数据完毕,等待其他线程写入完毕 Thread-0所有线程写入完毕,继续处理其他任务... Thread-3所有线程写入完毕,继续处理其他任务... Thread-1所有线程写入完毕,继续处理其他任务... Thread-2所有线程写入完毕,继续处理其他任务... CyclicBarrier重用 线程Thread-4正在写入数据... 线程Thread-5正在写入数据... 线程Thread-6正在写入数据... 线程Thread-7正在写入数据... 线程Thread-7写入数据完毕,等待其他线程写入完毕 线程Thread-5写入数据完毕,等待其他线程写入完毕 线程Thread-6写入数据完毕,等待其他线程写入完毕 线程Thread-4写入数据完毕,等待其他线程写入完毕 Thread-4所有线程写入完毕,继续处理其他任务... Thread-5所有线程写入完毕,继续处理其他任务... Thread-6所有线程写入完毕,继续处理其他任务... Thread-7所有线程写入完毕,继续处理其他任务...
|
semaphore:信号量
Semaphore实现的功能就类似厕所有5个坑,假如有10个人要上厕所,那么同时只能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中 的任何一个人让开后,
其中等待的另外5个人中又有一个人可以占用了。另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项。单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。
如果同时只允许一个线程来访问就可以实现Synchronized和lock的功能
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class Test { public static void main(String[] args) { // 线程池 ExecutorService exec = Executors.newCachedThreadPool(); // 只能5个线程同时访问 final Semaphore semp = new Semaphore(5); // 模拟20个客户端访问 for (int index = 0; index < 20; index++) { final int NO = index; Runnable run = new Runnable() { public void run() { try { // 获取许可 semp.acquire(); System.out.println("Accessing: " + NO); Thread.sleep((long) (Math.random() * 10000)); // 访问完后,释放 semp.release(); //availablePermits()指的是当前信号灯库中有多少个可以被使用 System.out.println("-----------------" + semp.availablePermits()); } catch (InterruptedException e) { e.printStackTrace(); } } }; exec.execute(run); } // 退出线程池 exec.shutdown(); } }
|
执行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| Accessing: 0 Accessing: 2 Accessing: 1 Accessing: 4 Accessing: 3 -----------------1 Accessing: 8 -----------------1 Accessing: 11 -----------------1 Accessing: 7 -----------------1 Accessing: 9 -----------------1 Accessing: 12 -----------------1 Accessing: 5 -----------------1 Accessing: 10 -----------------1 Accessing: 6 -----------------1 Accessing: 13 -----------------1 Accessing: 14 Accessing: 15 -----------------0 -----------------1 Accessing: 16 -----------------1 Accessing: 17 -----------------1 Accessing: 18 -----------------1 Accessing: 19 -----------------1 -----------------2 -----------------3 -----------------4 -----------------5
|