• CountDownLatch 的使用与源码解析


    使用

    CountDownLatch 类似于 Thread 的 join 方法,使用时,先构造 CountDownLatch 对象,构造函数传线程数据数 n,表示等待这 n 个线程都完成后再执行主线程代码。

    主线程使用 await 方法阻塞等待 n 个线程执行完成;n 个线程执行完成后调用 countDown() 方法,表示完成了一个线程,当 n 个线程都调用了一次 countDown() 方法后,await 阻塞的主线程就会被唤醒继续执行。

    Java8 的 CompletableFuture 也可以用来代替 CountDownLatch。

    代码示例:

    @Test
    public void test() throws InterruptedException {
        // 定义一个线程池
        ExecutorService executor = Executors.newFixedThreadPool(2);
    
        // CountDownLatch 用来作为一个开关,控制主线程阻塞,直到几个异步线程都完成后,主线程再接着执行
        // CountDownLatch 的构造函数传参是一个 int 型数据,等待几个异步线程,就传异步线程的数量即可
        // 比如,当前线程需要等待 3 个异步线程先执行完成后,再执行,那么传参为 3
        int count = 3;
        CountDownLatch countDownLatch = new CountDownLatch(count);
    
        IntStream.range(0, count).forEach(i -> {
            executor.submit(() -> {
                System.out.println("条件" + (i + 1) + "正在执行");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("条件" + (i + 1) + "执行完成");
    
                // 线程执行完成后,需要调用 countDown() 方法,告诉主线程当前有一个线程完成了
                countDownLatch.countDown();
            });
        });
    
        System.out.println("主线程阻塞等待条件完成");
        // 主线程等待条件完成,当所有线程都调用了 countDown() 后阻塞的主线程就会被唤醒,继续往下执行
        countDownLatch.await();
        System.out.println("条件都已完成,继续执行主线程逻辑");
    
        executor.shutdown();
    
        /**
             * 执行结果:
             *
             * 主线程阻塞等待条件完成
             * 条件2正在执行
             * 条件1正在执行
             * 条件1执行完成
             * 条件3正在执行
             * 条件2执行完成
             * 条件3执行完成
             * 条件都已完成,继续执行主线程逻辑
             */
    }
    

    源码

    属性

    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;
    
        // 共享线程数
        Sync(int count) {
            setState(count);
        }
    
        int getCount() {
            return getState();
        }
    
        // 尝试获取共享锁
        protected int tryAcquireShared(int acquires) {
            // state == 0 时返回 1,否则返回 -1
            return (getState() == 0) ? 1 : -1;
        }
    
        // 尝试释放共享锁
        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }
    
    private final Sync sync;
    

    构造函数

    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }
    

    await

    AQS 共享锁参考 ReentrantLock

    // CountDownLatch 构造方法,将传的参数 count 赋值给 state
    // 每次调用 countDown 方法时,会释放一个锁,即 state 减一
    // 如果主线程调用了 await 时会去判断,如果 state > 0,那么主线程就会被阻塞
    // 等待 count 个线程都调用了 countDown 方法后,才会唤醒主线程
    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    
    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }
    

    countDown

    // 线程释放一个共享锁,state 减一
    public void countDown() {
        // 会去唤醒阻塞的主线程
        // 主线程被唤醒后,如果条件不符合(state > 0)主线程还是会继续阻塞,直到最后一个线程调用了 countDown
        // 此时 state == 0,主线程被唤醒后不会再被阻塞
        sync.releaseShared(1);
    }
    
  • 相关阅读:
    【2020-01-28】陪伴即陪伴,擦汗即擦汗
    【2020-01-27】曼巴走了,但他还在
    【2020-01-26】今年,远亲不如近邻了
    【2020-01-25】新的一年,新的传统
    【2020-01-24】上天为这小女孩开了一扇小小窗
    【2020-01-23】故作假装的毛病
    day 31 html(二) 和css入门
    前端 day 30 html 基础一
    day 17python 面对对象之继承
    多并发编程基础 之协成
  • 原文地址:https://www.cnblogs.com/wu726/p/15643907.html
Copyright © 2020-2023  润新知