将上述案例中的读写锁改成可重入锁,即将第行代码注释掉那么所有的读和写线程都必须相互等待,程序执行时间如下所示
倒计时器:CountDownLatch
CountDownLatch是java1.5版本之后util.concurrent提供的工具类。这里简单介绍一下CountDownLatch,可以将其看成是一个计数器,await()方法可以阻塞至超时或者计数器减至0,其他线程当完成自己目标的时候可以减少1,利用这个机制我们可以将其用来做并发。 比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。
CountDownLatch类只提供了一个构造器,该构造器接受一个整数作为参数,即当前这个计数器的计数个数 。
public CountDownLatch(int count) { }; //参数count为计数值
使用场景:比如对于马拉松比赛,进行排名计算,参赛者的排名,肯定是跑完比赛之后,进行计算得出的,翻译成Java识别的预发,就是N个线程执行操作,主线程等到N个子线程执行完毕之后,在继续往下执行。
public class CountDownLatchTest { public static void main(String[] args){ int threadCount = 10; final CountDownLatch latch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { new Thread(new Runnable() { @Override public void run() { System.out.println("线程" + Thread.currentThread().getId() + "开始出发"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程" + Thread.currentThread().getId() + "已到达终点"); latch.countDown(); } }).start(); } try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("10个线程已经执行完毕!开始计算排名"); } }
View Code结果如下
线程12开始出发 线程14开始出发 线程15开始出发 线程17开始出发 线程13开始出发 线程16开始出发 线程18开始出发 线程19开始出发 线程20开始出发 线程21开始出发 线程16已到达终点 线程13已到达终点 线程19已到达终点 线程18已到达终点 线程17已到达终点 线程14已到达终点 线程15已到达终点 线程12已到达终点 线程21已到达终点 线程20已到达终点 10个线程已经执行完毕!开始计算排名
View CodeCountDownLatch在并行化应用中也是比较常用。常用的并行化框架OpenMP中也是借鉴了这种思想。比如有这样的一个需求,在你淘宝订单的时候,这笔订单可能还需要查,用户信息,折扣信息,商家信息,商品信息等,用同步的方式(也就是串行的方式)流程如下。
设想一下这5个查询服务,平均每次消耗100ms,那么本次调用至少是500ms,我们这里假设,在这个这五个服务其实并没有任何数据依赖,谁先获取谁后获取都可以,那么我们可以想办法并行化这五个服务。
这里可以使用CountDownLatch来实现这个效果。