Java併發之CyclicBarrier & CountDownLatch

Java併發之CyclicBarrier & CountDownLatch

CountDownLatch

“同生共死” 用於保持線程間的同時開始,初始化一個整形的信號量,每個信號再開始之前 使信號量減一,當信號量不爲0時,所有線程被阻塞,直到最後一個線程使信號量爲0。使其達到線程同時發生。相比 Semaphore,CountDownLatch除了控制線程併發數,也能保證線程同時執行。

  • 操作
    • CountDownLatch(int count) 並發信號量數目
    • await() 永久阻塞直到信號量爲零
    • await(long timeout, TimeUnit unit) 阻塞到最大延長時間或者信號量爲零
    • countDown() 信號量減一

CyclicBarrier

“兄弟先上,我殿後”,類似CountDownLatch ,包含其所有功能,另實現了,同步完成後,執行的特定任務。

  • 操作
    • CyclicBarrier(int parties) 功能同CountDownLatch
    • CyclicBarrier(int parties, Runnable barrierAction)
    • await() 永久阻塞直到信號量爲零
    • await(long timeout, TimeUnit unit) 阻塞到最大延長時間(會拋出錯誤提示,但會繼續執行)或者信號量爲零

CODE

package com.liaojl.test.concurrent;

import lombok.SneakyThrows;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

public class Test2 {
    private static final Logger log = LoggerFactory.getLogger(Test2.class);
    public static final int SIZE = 5;

    /**
     * 同生共死
     *
     * @throws InterruptedException
     */
    @Test
    public void countDownLatchTest() throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        Thread thread = null;
        for (int i = 0; i < SIZE + 1; i++) {
            if (i > 1 && i < 3) {
                thread = new Thread(new MyThread(countDownLatch, 1));
            } else {
                thread = new Thread(new MyThread(countDownLatch));
            }
            thread.setName("線程" + (i + 1));
            thread.start();
        }
        Thread.sleep(100);
        log.info("傻了吧,阻塞住了");
        Thread.sleep(100);
        log.info("開始了");
        countDownLatch.countDown();
        Thread.sleep(100);


    }

    private static class MyThread implements Runnable {
        private static final Logger log = LoggerFactory.getLogger(MyThread.class);
        private final CountDownLatch countDownLatch;
        private long timeout = 0;

        public MyThread(CountDownLatch countDownLatch) {
            this.countDownLatch = countDownLatch;
        }

        public MyThread(CountDownLatch countDownLatch, long timeout) {
            this.countDownLatch = countDownLatch;
            this.timeout = timeout;
        }

        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see Thread#run()
         */
        @SneakyThrows
        @Override
        public void run() {
            /**
             * 線程阻塞
             */
            if (timeout < 1) {
                countDownLatch.await();
            } else {
                countDownLatch.await(timeout, TimeUnit.NANOSECONDS);
                log.info("lz受不了,先走了");
            }
            log.info("線程名:{} 總數:{},時間:{} 一起去打怪!!!", Thread.currentThread().getName(), countDownLatch.getCount(), System.currentTimeMillis());
        }
    }

    /**
     * 有困難兄弟線上,我收人頭
     */
    @Test
    public void cyclicBarrierTest() throws InterruptedException {
//        CyclicBarrier cyclicBarrier = new CyclicBarrier(SIZE);
        CyclicBarrier cyclicBarrier = new CyclicBarrier(SIZE,
                () -> log.info("線程名:{} , 時間:{} 坐等收人頭!!!",
                        Thread.currentThread().getName(),
                        System.currentTimeMillis()));
        Thread thread = null;
        /**
         * 當最後一個 await 完成 阻塞才完成
         * true  最先執行 但後續的變爲同步任務,他們要同步執行完成
         * false 最後執行,其執行順序爲隨意,任務的執行時間 是他們最後一個完成await 的時間
         */
        boolean iAmFirst = false;
        for (int i = 0; i < SIZE; i++) {
            if (2 > i && i < 4) {
                /**
                 * 當阻塞超時是 拋出錯誤異常,但不會影響後續執行,僅僅是錯誤提醒
                 * <code>
                 *    Exception in thread "線程2" java.util.concurrent.TimeoutException
                 * 	at java.util.concurrent.CyclicBarrier.dowait(CyclicBarrier.java:257)
                 * 	at java.util.concurrent.CyclicBarrier.await(CyclicBarrier.java:435)
                 * 	at com.liaojl.test.concurrent.Test2$MyThread2.run(Test2.java:180)
                 * 	at java.lang.Thread.run(Thread.java:748)
                 * </code>
                 */
                thread = new Thread(new MyThread2(cyclicBarrier, 1, iAmFirst));
            } else {
                thread = new Thread(new MyThread2(cyclicBarrier, iAmFirst));
            }
            thread.setName("線程" + (i + 1));
            thread.start();
        }
        Thread.sleep(5000);
    }

    private static class MyThread2 implements Runnable {
        private static final Logger log = LoggerFactory.getLogger(MyThread.class);
        private final CyclicBarrier cyclicBarrier;
        private boolean first;
        private long timeout = 0;

        /**
         * @param cyclicBarrier
         * @param first         是否最先執行
         */
        public MyThread2(CyclicBarrier cyclicBarrier, boolean first) {
            this.cyclicBarrier = cyclicBarrier;
            this.first = first;
        }

        /**
         * @param cyclicBarrier
         * @param timeout       延遲時間 納秒
         * @param first         是否最先執行
         */
        public MyThread2(CyclicBarrier cyclicBarrier, long timeout, boolean first) {
            this.cyclicBarrier = cyclicBarrier;
            this.timeout = timeout;
            this.first = first;
        }

        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see Thread#run()
         */
        @SneakyThrows
        @Override
        public void run() {
            long millis = (long) (Math.random() * 1000);
            Thread.sleep(millis);
            if (first) {
                /**
                 * 線程阻塞
                 */
                if (timeout < 1) {
                    cyclicBarrier.await();
                } else {
                    cyclicBarrier.await(timeout, TimeUnit.NANOSECONDS);
                    log.info("線程名:{}  lz不當炮灰,先走了!!!",
                            Thread.currentThread().getName());
                }
            }
            log.info("線程名:{} 總數:{},到達屏障總數:{},時間:{} 一起去打怪!!!",
                    Thread.currentThread().getName(),
                    cyclicBarrier.getParties(),
                    cyclicBarrier.getNumberWaiting(),
                    System.currentTimeMillis());
            if (!first) {
                /**
                 * 線程阻塞
                 */
                if (timeout < 1) {
                    cyclicBarrier.await();
                } else {
                    cyclicBarrier.await(timeout, TimeUnit.NANOSECONDS);
                    log.info("線程名:{} lz不當炮灰,先走了!!!",
                            Thread.currentThread().getName());
                }
            }
        }
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章