CountDownLatch的原理

来源:互联网 发布:nginx tomcat 宕机 编辑:程序博客网 时间:2024/06/13 21:21

    今天在学习ZooKeeper的实例的时候,发现了CountDownLatch这个类,那么这个类又是如何使用的呢?以及这个类的原理是什么?我们首先看一下简单的demo:

public class CountDownLatchTest {    static class Student implements Runnable {        CountDownLatch  countDownLatch  = null;        /**         *          */        public Student(CountDownLatch countDownLatch) {            this.countDownLatch = countDownLatch;        }        /*         * (non-Javadoc)         *          * @see java.lang.Runnable#run()         */        @Override        public void run() {            try {                Thread.sleep( 1000 );                System.out.println( "交卷了!" );                countDownLatch.countDown();            } catch (Exception e) {                // TODO: handle exception            }        }    }    static class Teacher implements Runnable {        CountDownLatch  countDownLatch  = null;        /**         *          */        public Teacher(CountDownLatch countDownLatch) {            this.countDownLatch = countDownLatch;        }        /*         * (non-Javadoc)         *          * @see java.lang.Runnable#run()         */        @Override        public void run() {            try {                countDownLatch.await();                System.out.println( "完成考试了" );            } catch (Exception e) {                // TODO: handle exception            }        }    }    public static void main(String args[]) {        CountDownLatch countDownLatch = new CountDownLatch( 4 );        Executor executor = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors() + 1 );        executor.execute( new Student( countDownLatch ) );        executor.execute( new Student( countDownLatch ) );        executor.execute( new Student( countDownLatch ) );        executor.execute( new Student( countDownLatch ) );        executor.execute( new Teacher( countDownLatch ) );    }}

    如上面所示,我们首先定义了定义了一个Student的线程,然后在休眠一段时间,执行CountDownLatch.countDown(),然后定义了Teacher的线程,直接等待。CountDownLatch的值降低到0自动会唤醒。
    其实CountDownLatch类似于一个计数器,只不过这个计数器同时只有一个线程去操作。
    这个类适用于当某个线程等待其他n个线程完成任务的时候再执行相应的任务。
    那么CountDownLatch的实现原理是什么呢?我们来看一下源码,当我们初始化CountDownLatch的时候会发生什么事情:

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

    CountDownLatch在初始化的时候,将内部变量sync进行赋值,sync的变量类型是Sync,那么我们看看Sync的实现

 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) {            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;            }        }    }

    我们可以看到Sync也是基于AQS实现的共享锁。那么当调用CountDownLatch.countDown()会发生什么?

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

    可以看出来,CountDownLatch.countDown()就是调用AQS的releaseShared(int arg),然后releaseShared(int arg)可以保证将数量原子地减一。
    那么CountDownLatch.await()又是如何实现的呢?

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

    await()调用的是sync的acquireSharedInterruptibly(int arg)

    public final void acquireSharedInterruptibly(int arg)            throws InterruptedException {        if (Thread.interrupted())            throw new InterruptedException();        if (tryAcquireShared(arg) < 0)            doAcquireSharedInterruptibly(arg);    }

    在Sync重写了tryAcquireShard(arg),当计数器数字不为0的时候返回-1,因此调用doAcquireShardInterruptibly(arg),而doAcquireShardInterruptibly则会将本线程加载到执行链条的末尾,当别的线程执行完后才会执行,关于AQS的源码解析我们下个文章再看。

0 0
原创粉丝点击