Java并发系列—工具类:CountDownLatch

349 阅读2分钟

CountDownLatch也叫闭锁,在JDK1.5被引入,CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行。

使用示例

CountDownLatch的构造函数接收一个int类型的参数作为计数器,如果你想等待***N个点***完成,这里就传入N。当调用CountDownLatch的countDown方法时,N就会减1,CountDownLatch的await方法会阻塞当前线程,直到N变成零。

N个点:由于CountDownLatch方法可以用在任何地方,这里说的N个点,可以是N个线程,也可以是1个线程里的N个执行步骤。用在多个线程时,只需要把CountDownLatch的引用传递到线程里即可。

public class CountDownLatchDemo {

    final static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch=new CountDownLatch(2);//两个工人的协作
        Worker worker1=new Worker("zhang san", 5000, latch);
        Worker worker2=new Worker("li si", 8000, latch);
        worker1.start();//
        worker2.start();//
        latch.await();//等待所有工人完成工作
        System.out.println("all work done at "+sdf.format(new Date()));
    }


    static class Worker extends Thread{
        String workerName;
        int workTime;
        CountDownLatch latch;
        
        public Worker(String workerName ,int workTime ,CountDownLatch latch){
             this.workerName=workerName;
             this.workTime=workTime;
             this.latch=latch;
        }
        
        public void run(){
            System.out.println("Worker "+workerName
                +" do work begin at "+sdf.format(new Date()));
            doWork();//工作了
            System.out.println("Worker "+workerName
                +" do work complete at "+sdf.format(new Date()));
            latch.countDown();//工人完成工作,计数器减一

        }

        private void doWork(){
            try {
                Thread.sleep(workTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

实现原理

CountDownLatch内部会维护一个初始值为线程数量的计数器,主线程执行await方法,如果计数器大于0,则阻塞等待。当一个线程完成任务后,计数器值减1。当计数器为0时,表示所有的线程已经完成任务,等待的主线程被唤醒继续执行。CountDownLatch实现主要基于java同步器AQS,其内部维护一个AQS子类,并重写了相关方法。

private static final class Sync extends AbstractQueuedSynchronizer {
    
    Sync(int count) {
        setState(count);
    }

    int getCount() {
        return getState();
    }
    
    
    protected int tryAcquireShared(int acquires) {
        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;
        }
    }
}

await方法源码如下:

public void await() throws InterruptedException {
     sync.acquireSharedInterruptibly(1);
}

await方法执行过程流程图如下:

countDown方法源如下:

public void countDown() {
    sync.releaseShared(1);
}

countDown方法执行过程和await方法相似,不具体分析。

注意事项

  • CountDownLatch不可能重新初始化或者修改CountDownLatch对象的内部计数器的值。

欢迎留言补充,共同交流。个人微信公众号求关注