zoukankan      html  css  js  c++  java
  • CountDownLatch(三)

    CountDownLatch简介

    (1)用于解决什么问题?

    在并发编程的场景中,最常见的一个case是某个任务的执行,需要等到多个线程都执行完毕之后才可以进行,CountDownLatch可以很好解决这个问题。

    (2)使用说明

    同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

    (3)CountDownLatch的常用方法

    1.构造方法:

    // 构造器,必须指定一个大于零的计数
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        // Sync 就是继承了一个AQS
        this.sync = new Sync(count);
    }
    

    2.await方法

    // 线程阻塞,直到计数为0的时候唤醒;可以响应线程中断退出阻塞
    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    // 线程阻塞一段时间,如果计数依然不是0,则返回false;否则返回true
    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }
    

    await内部实现流程:

    1. 判断state计数是否为0,不是,则直接放过执行后面的代码;
    2. 大于0,则表示需要阻塞等待计数为0;
    3. 当前线程封装Node对象,进入阻塞队列;
    4. 然后就是循环尝试获取锁,直到成功(即state为0)后出队,继续执行线程后续代码。

    await是通过轮询state的状态来判断所有的任务是否都完成。

    3.countDown方法

    // 计数-1
    public void countDown() {
        sync.releaseShared(1);
    }
    

    countDown内部实现流程:

    1. 尝试释放锁tryReleaseShared,实现计数-1
    • 若计数已经小于0,则直接返回false;
    • 否则执行计数(AQS的state)减一;
    • 若减完之后,state==0,表示没有线程占用锁,即释放成功,然后就需要唤醒被阻塞的线程了;
    1. 释放并唤醒阻塞线程 doReleaseShared;
    • 如果队列为空,即表示没有线程被阻塞(也就是说没有线程调用了 CountDownLatch#wait()方法),直接退出;
    • 头结点如果为SIGNAL, 则依次唤醒头结点下个节点上关联的线程,并出队;

    4.getCount方法

    // 获取计数
    public long getCount() {
        return sync.getCount();
    }
    

    (4)小例子:计算多个线程并发运行的时间。

    public class TestCountDownLatch {
    
        public static void main(String[] args) throws InterruptedException {
            long start = System.currentTimeMillis();
            CountDownLatch countDownLatch = new CountDownLatch(5);
            CountDownLatchThread countDownLatchThread = new CountDownLatchThread(countDownLatch);
            for (int i = 0; i < 5; i++){
                new Thread(countDownLatchThread).start();
            }
            // 主线程等待
            countDownLatch.await();
            long end = System.currentTimeMillis();
            System.out.println("time : " + (end - start));
        }
    }
    
    class CountDownLatchThread implements Runnable{
        private CountDownLatch countDownLatch = null;
        public CountDownLatchThread(CountDownLatch countDownLatch){
            this.countDownLatch = countDownLatch;
        }
        @Override
        public void run() {
            synchronized (this){
                // 加入try确保countDownLatch.countDown()一定会执行
                try {
                    for (int i = 0; i < 10; i++){
           System.out.println(Thread.currentThread().getName() + " : " + i);
                    } 
                }finally {
                    countDownLatch.countDown();
                }  
            }
        }
    }
    

    参考文献

    本片文章,主要整理自互联网,便于自己复习知识所用,以下为参考链接!

    【1】Java并发学习之CountDownLatch实现原理及使用姿势

    【2】CountDownLatch源码解析

  • 相关阅读:
    一致性hash 参考:http://blog.csdn.net/cywosp/article/details/23397179/
    动态规划:最长公共子序列和最长公共子串
    并发包(转) http://blog.csdn.net/he90227/article/details/52788981
    海量数据
    MyBatis
    Spring MVC执行流程
    mysql优化
    红黑树
    并发包 (转) http://www.cnblogs.com/wanly3643/category/437878.html
    递增数列中找出和为s的数列
  • 原文地址:https://www.cnblogs.com/ch-forever/p/10218007.html
Copyright © 2011-2022 走看看