CountDownLatch的实现原理

在日常开发中经常会遇到需要在主线任务中开启多个线程,并等到所有子线程完成工作之后再进行汇总的情形,这种情况下可以考虑使用CountDownLatch这个工具类实现功能,下面是使用CountDownLatch的一个示例:

package xiancheng.Lock;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchTest {
    public static void main(String[] args) {
        //创建一个CountDownLatch实例,计数器记为3
        final CountDownLatch latch=new CountDownLatch(3);
        new Thread(){
            public void run(){
                try{
                    System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
                    Thread.sleep(3000);
                    System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
                    latch.countDown();
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }.start();

        new Thread(){
            public void run(){
                try{
                    System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
                    Thread.sleep(3000);
                    System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
                    latch.countDown();
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }.start();

        new Thread(){
            public void run(){
                try{
                    System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
                    Thread.sleep(3000);
                    System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
                    latch.countDown();
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }.start();

        try {
            System.out.println("等待3个子线程执行完毕...");
            latch.await();
            System.out.println("3个子线程已经执行完毕");
            System.out.println("继续执行主线程");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

运行结果:
子线程Thread-0正在执行
子线程Thread-1正在执行
等待3个子线程执行完毕...
子线程Thread-2正在执行
子线程Thread-0执行完毕
子线程Thread-1执行完毕
子线程Thread-2执行完毕
3个子线程已经执行完毕
继续执行主线程

以上示例很清晰地反映了CountDownLatch类的使用方法,上述代码其实还不够简洁,如果使用线程池代码会更优雅。从CountDownLatch这个类名可以推测,该类内部应该是有一个计数器,并且这个计数器是递减的,实际上CountDownLatch就是通过前面所提到的AQS组件实现的,下面来看看该类的源代码:

public class CountDownLatch {
    //实现AQS组件的内部类,可以看出CountDownLatch是通过AQS实现的
    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;
        

        //设置计数器实际上是将值赋给了AQS状态变量state
        Sync(int count) {
            setState(count);
        }
        
        //获取状态变量state的值
        int getCount() {
            return getState();
        }

        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }

        protected boolean tryReleaseShared(int releases) {
            //循环进行CAS,直到当前线程完成CAS减去1操作
            for (;;) {
                int c = getState();
                //当前状态值为0则直接返回
                if (c == 0)
                    return false;
                int nextc = c-1;
                //使用CAS让计数器值减去1
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }

    private final Sync sync;

    //构造方法调用Sync类的构造函数
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

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

    //调用该方法后线程会被阻塞,直到发生下列情况之一才会返回:
        1所有线程调用countdown方法,计数器的值变为0
        2设置的时间到了,超时返回
        3其他线程调用了当前线程的interrupt()方法中断了当前线程
    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }

    //
    public void countDown() {
        调用sync类的方法,使state的值减去1
        sync.releaseShared(1);
    }

    //调用该方法获得state的值,一般在测试的时候使用
    public long getCount() {
        return sync.getCount();
    }

    
    public String toString() {
        return super.toString() + "[Count = " + sync.getCount() + "]";
    }
}

总结一下CountDownLatch的工作原理:CountDownLatch使使用AQS实现的,使用AQS的state变量来存放计数器的值。在调用CountDownLatch的构造函数时,会调用内部类Sync的构造函数将值赋给state变量,当多个线程调用countdown方法时实际是使用CAS递减state变量的值;当线程调用await方法后当前线程会被放入AQS阻塞队列等待计数器为0时返回,即所有线程都调用了countdown方法时。最后,当计数器的值变为0时,当前线程还会调用AQS的doReleasedShared()方法激活调用await()方法而被阻塞的线程。

你可能感兴趣的:(java多线程)