juc包之CountDownLatch与CyclicBarrier

CountDownLatch与CyclicBarrier各自的实现原理

CountDownLatch类
示例代码: (在CountDownLatch类的注释上方提供了两种使用方式)

  • 第一种

    class Driver {
    
        void main() throws InterruptedException {
            CountDownLatch startSignal = new CountDownLatch(1);
            CountDownLatch doneSignal = new CountDownLatch(N);
    
            for (int i = 0; i < N; ++i) // create and start threads
                new Thread(new Worker(startSignal, doneSignal)).start();
    
            doSomethingElse();
            startSignal.countDown();
            doSomethingElse();
            doneSignal.await();
        }
    }
    
    class Worker implements Runnable {
        private final CountDownLatch startSignal;
        private final CountDownLatch doneSignal;
        Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
            this.startSignal = startSignal;
            this.doneSignal = doneSignal;
        }
        public void run() {
            try {
                startSignal.await();
                doWork();
                doneSignal.countDown();
            } catch (InterruptedException ex) {}
        }
    
        void doWork() {
            //something to do
        }
    }
    

先让所有Worker等Driver去唤醒,然后Driver等所有Worker执行完再唤醒

  • 第二种

    class Driver2 { // ...
        void main() throws InterruptedException {
            CountDownLatch doneSignal = new CountDownLatch(N);
            Executor e = ...
    
            for (int i = 0; i < N; ++i) // create and start threads
                e.execute(new WorkerRunnable(doneSignal, i));
    
            doneSignal.await();           // wait for all to finish
        }
    }
    
    class WorkerRunnable implements Runnable {
        private final CountDownLatch doneSignal;
        private final int i;
        WorkerRunnable(CountDownLatch doneSignal, int i) {
            this.doneSignal = doneSignal;
            this.i = i;
        }
        public void run() {
            try {
                doWork(i);
                doneSignal.countDown();
            } catch (InterruptedException ex) {} // return;
        }
    
        void doWork() { ... }
    }  
    

让Drive等待所有Worker运行完毕(注释说这种写法可以用CyclicBarrier代替)

看一下几个核心方法

  • CountDownLatch构造方法

upload successful

upload successful
初始化主要是给state变量设置了值,并且该值不能小于0

  • CountDownLatch的countdown方法

upload successful

upload successful
countdown执行了Sync内部类的releaseShared方法,该方法先执行了tryReleaseShared方法,返回true才会去执行doReleaseShared方法

upload successful
该方法首先判断state值是否为0,如果为0,直接返回false,代表没有资源可以释放了,要是不为0,则将state值减1,并将结果更新给state变量,然后判断-1后的值是否为0,如果为0,则返回true.所以返回true的含义就是在此次释放资源后已经没有需要释放的资源了.

upload successful
该方法是在资源全部被释放后才会执行的.首先获取队列的head节点,并且判断head节点不为null也不为tail(head只有已经唤醒过才有可能与tail节点相同).然后获取head的waitStatus.如果waitStatus是Signal则会去唤醒该节点线程,如果执行期间head节点被替换了,则会继续循环,否则就跳出.

  • CountDownLatch的await方法

upload successful

upload successful
await执行了Sync内部类的acquireSharedInterruptibly方法,该方法首先判断当前线程是否中断了,如果中断直接抛出错误,否则执行tryAcquireShared方法,当返回负数时才会去执行doAcquireSharedInterruptibly方法

upload successful
判断当前state变量值是否为0,只有当不为0才会返回负数-1,代表还有资源未释放

upload successful
该方法内部与ReentranLock类的acquiredQueued很像,都是遍历链表节点,去获取资源,获取失败则会调用LockSupport的park方法将其挂起,且设置节点为SIGNAL.获取成功则会调用setHeadAndPropagate方法将所有未取消的节点都唤醒

作者

windwest

发布于

2022-01-19

更新于

2022-01-19

许可协议

评论