Java并发多线程_Countdownlatch使用详解


  之前在刷题的时候有遇到这样一个编程题:100个人同时赛跑,得到前十名的排行榜。可谓是抓耳挠腮,不知怎么办。后面接触了并发类Countdownlatch,作一个demo记录该如何使用Countdownlatch。

  Countdownlatch是利用计数器来实现并发开始、结束的,在构造方法中需要设置计数器大小,每次一个线程执行完毕就将计数器减1,当这个计数器

为0时,就会继续往下执行,否则等待。

      构造函数及内部类Sync (这个Sync看到后面越看越懵。2020-07-02更新:CAS懂了,AQS还是懵的)

     //内部类,await,countdown都是基于它实现的
     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;
            }
        }
    }

    private final Sync sync;

    /**
     * Constructs a {@code CountDownLatch} initialized with the given count.
     *
     * @param count the number of times {@link #countDown} must be invoked
     *        before threads can pass through {@link #await}
     * @throws IllegalArgumentException if {@code count} is negative
     */
    //构造方法
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

  现在我们来尝试100个人同时赛跑,然后打印他们完成的顺序。定义一个begin,用来记录同时开始,计数器为1。再定义一个end,计数器为100,

用来记录结束的标志。线程状态为:

  1. 当for循环执行完毕,这个时候begin的计数器为1,begin还在await等待,100个人在预备。
  2. begin.countdown,begin的计数器变为0,begin不再await,100个人开始赛跑。
  3. 每个人跑完,end就执行一个countdown,100,99,98....
  4. end的计数器变为0,主线程main继续往下执行。
public static void main(String[] args){
        CountDownLatch begin = new CountDownLatch(1);     //1.用来同时开始,如果不需要同时开始,那么就不需要这个。
        CountDownLatch end = new CountDownLatch(100);      //2.用来结束标记,当计数器减为0的时候,就继续往下执行
        for (int i=0;i<100;i++){
            ThreadTest threadTest = new ThreadTest(begin,end);
            threadTest.start();
        }
        try{
            System.out.println("begin....");
            begin.countDown();                           //3.所有线程在等待了,见4。减1后,4处等待的线程开始执行
            end.await();                                 //4.等待5处所有线程都减1,即计数器变为0,就继续往下执行
            System.out.println("end....");
        }catch (Exception e){

        }
    }
    static class ThreadTest extends Thread{
        CountDownLatch begin;
        CountDownLatch end;
        ThreadTest(CountDownLatch begin,CountDownLatch  end){
            this.begin=begin;
            this.end=end;
        }
        public void run(){
            try{
                begin.await();                         //4.所有线程等待,因为begin的计数器还是1
            //    Thread.sleep(5000);
                for(int i=1;i<1000;i++){
                    i=i*i;
                }
                System.out.println(Thread.currentThread().getName()+"ok");
                end.countDown();                       //5.线程执行完了,end的计数器就减1
            }catch (Exception e){

            }
        }
    }

  Countdownlatch使用就这么简单不看Sync原理的话,await还有另一种方式,可设置等待时间,防止线程里运行时间过久影响主线程,或死锁的发生。

public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM