Java多线程系列9(CountDownLatch)
来源:互联网 发布:淘宝卖家手册pdf 编辑:程序博客网 时间:2024/06/08 04:59
1 CounDownLatch介绍
CountDownLatch是java 1.5新引入的一个同步工具类,常用场景为:一个(或者多个)线程等待另外N个线程完成某个事情之后才能执行。是一个非常有用的线程同步类。
其API有:
//递减锁存器的计数值,如果计数达到0,就释放所有等待的线程。如果计数大于0,计数减1public void countDown()//等待锁存器的计数值减为0public void await()
2实际例子
2.1 场景一
考虑这样一个实际的问题:公司组织一个会议,与会者10名,只有当10名与会者全部到齐,会议才能开始。
这个场景,用CountDownLatch可以很方便实现:
这public class TestCountDownLatch { private static final int PEOPLE_NUM = 10; public static CountDownLatch latch = new CountDownLatch(PEOPLE_NUM); private static AtomicInteger index = new AtomicInteger(0); public static class People implements Runnable { private CountDownLatch latch; private int index; public People(CountDownLatch latch, int index) { this.latch = latch; this.index = index; } @Override public void run() { latch.countDown(); System.out.println("People index = " + index + " arrival."); } } public static class Conference implements Runnable { private CountDownLatch latch; public Conference(CountDownLatch latch) { this.latch = latch; } @Override public void run() { System.out.println("Conference begin wait all people arrival"); try { latch.await(); } catch (InterruptedException e) { } System.out.println("Conference started"); } } public static void main(String[] args) { Thread conferenceThread = new Thread(new Conference(latch)); conferenceThread.start(); for (int i = 0; i < PEOPLE_NUM; i++) { Thread thread = new Thread(new People(latch, index.addAndGet(1))); thread.start(); } }}
运行结果:
Conference begin wait all people arrivalPeople index = 1 arrival.People index = 2 arrival.People index = 3 arrival.People index = 4 arrival.People index = 5 arrival.People index = 6 arrival.People index = 7 arrival.People index = 8 arrival.People index = 9 arrival.People index = 10 arrival.Conference started
从运行结果可以看到调用await以后,Conference会等待其余10个线程调用countDown,当计数为0时,Conference会从 await点继续向下执行。
Android系统中很多地方都用到了这样的设计。
比如SharedPreferences.java中:
public boolean commit() { MemoryCommitResult mcr = commitToMemory(); SharedPreferencesImpl.this.enqueueDiskWrite( mcr, null /* sync write on this thread okay */); try { mcr.writtenToDiskLatch.await(); } catch (InterruptedException e) { return false; } notifyListeners(mcr); return mcr.writeToDiskResult; }.....省略无关代码 public void setDiskWriteResult(boolean result) { writeToDiskResult = result; writtenToDiskLatch.countDown(); }
实现commit()同步方法的时候,会调用await等待写执行,写入磁盘的操作完成时,会调用countDown(),这样就保证commit()返回的时候,数据已经被写入磁盘。从而保证数据不会出现问题。
2.2 场景二
考虑这样一个场景:Android执行代码时,如何将一段异步代码转为同步代码?
这个场景,用CountDownLatch也能很好的予以解决。Android插件花框架Replugin的一段代码就是这个场景的一个实现:
public class ThreadUtils { private static Handler sHandler = new Handler(Looper.getMainLooper()); /** * 确保一定在主线程中使用 * <p> * 若当前处于主线程,则直接调用。若当前处于其它线程,则Post到主线程后等待结果 * * @param callable Callable对象 * @param wait 最长等待主线程的时间 * @param <T> 任何Object子类均可以 * @return 主线程执行完方法后,返回的结果 */ public static <T> T syncToMainThread(final Callable<T> callable, int wait) throws Throwable { if (sHandler.getLooper() == Looper.myLooper()) { // 已在UI线程中使用,则直接调用它 return callable.call(); } else { // 不在UI线程,需尝试Post到UI线程并等待 return syncToMainThreadByOthers(callable, wait); } } private static <T> T syncToMainThreadByOthers(final Callable<T> callable, int wait) throws Throwable { final AtomicReference<T> result = new AtomicReference<>(); final AtomicReference<Throwable> ex = new AtomicReference<>(); // 异步转同步 final CountDownLatch latch = new CountDownLatch(1); // 必须在主线程进行 sHandler.post(new Runnable() { @Override public void run() { try { result.set(callable.call()); } catch (Throwable e) { ex.set(e); } latch.countDown(); } }); try { latch.await(wait, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { // ignore } // 若方法体有异常?直接抛出 Throwable exo = ex.get(); if (exo != null) { throw exo; } // 没有问题?则直接返回结果 return result.get(); }}
syncToMainThreadByOthers中的实现,可以看到如何将一段代码由异步转为同步来执行。
2.3 场景三
以上都是一个线程等待N个线程执行完毕。CountDownLatch也可以适用于多个线程等待N个线程的场景。
比如小组的员工出差,需要组长审批,总监审批完毕以后,才可以出差。用CountDownLatch也能很好的予以解决:
代码如下:
public class TestCountDownLatch { private static final int ROUTINE_NUM = 2; private static final int STAFF_NUM = 10; public static CountDownLatch latch = new CountDownLatch(ROUTINE_NUM); private static AtomicInteger index = new AtomicInteger(0); public static class Manager implements Runnable { private CountDownLatch latch; public Manager(CountDownLatch latch) { this.latch = latch; } @Override public void run() { latch.countDown(); System.out.println("Manager agree the business trip"); } } public static class Leader implements Runnable { private CountDownLatch latch; public Leader(CountDownLatch latch) { this.latch = latch; } @Override public void run() { latch.countDown(); System.out.println("Leader agree the business trip"); } } public static class Staff implements Runnable { private CountDownLatch latch; private int index; public Staff(CountDownLatch latch, int index) { this.latch = latch; this.index = index; } @Override public void run() { System.out.println("Staff index = " + index + " begin wait business trip"); try { latch.await(); } catch (InterruptedException e) { } System.out.println("Staff index = " + index + " can go now"); } } public static void main(String[] args) { for (int i = 0; i < STAFF_NUM; i++) { Thread thread = new Thread(new Staff(latch, index.addAndGet(1))); thread.start(); } Thread manager = new Thread(new Manager(latch)); manager.start(); Thread leader = new Thread(new Leader(latch)); leader.start(); }}
测试结果如下:
Staff index = 1 begin wait business tripStaff index = 3 begin wait business tripStaff index = 4 begin wait business tripStaff index = 2 begin wait business tripStaff index = 5 begin wait business tripStaff index = 6 begin wait business tripStaff index = 7 begin wait business tripStaff index = 8 begin wait business tripStaff index = 9 begin wait business tripStaff index = 10 begin wait business tripManager agree the business tripLeader agree the business tripStaff index = 3 can go nowStaff index = 1 can go nowStaff index = 4 can go nowStaff index = 5 can go nowStaff index = 2 can go nowStaff index = 7 can go nowStaff index = 8 can go nowStaff index = 9 can go nowStaff index = 10 can go nowStaff index = 6 can go now
可以看到Staff 10个线程等待Manager/Leader两个线程审批出差申请,两个审批步骤通过以后,10个等待的Staff员工就可以出差了。
- Java多线程系列9(CountDownLatch)
- Java多线程系列-CountDownLatch
- Java多线程系列-CountDownLatch
- 【Java】多线程系列(二)之CountDownLatch的使用
- Java多线程系列(九)—CountDownLatch源码分析
- java多线程系列(八)---CountDownLatch和CyclicBarrie
- JAVA多线程之(CountDownLatch)
- JAVA多线程系列--并发工具类(CountDownLatch, CyclicBarrier, Semaphore,Exchanger)
- Java多线程之CountDownLatch(一)
- Java多线程之CountDownLatch
- Java多线程之CountDownLatch
- java 多线程 CountDownLatch用法
- Java多线程之CountDownLatch
- JAVA多线程之CountDownLatch
- java 多线程 CountDownLatch用法
- Java多线程之CountDownLatch
- java 多线程 CountDownLatch用法
- java 多线程 CountDownLatch用法
- Java多线程系列7(Condition)
- 关于ssm通过ajax来进行数据交互出现的错误:org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'ap
- Keepalived+LVS | Keepalived+Nginx
- RocketMq有3中消息类型
- Java多线程系列8(Atomic)
- Java多线程系列9(CountDownLatch)
- 数据结构学习之路6 用链表实现堆栈
- 抽象工厂模式(Abstract Factory Pattern)
- RocketMQ批量消费、消息重试、消费模式、刷盘方式
- 草根学Python(九) 面向对象
- nodejs socket实现的服务端和客户端简单通信
- Error:java.util.concurrent.ExecutionException: com.android.ide.common.process.ProcessException:
- 生产者消费者三种并发模式实现方法
- Redis有序集合