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