Java中的3個常用併發工具類

(一)等待多線程完成的CountDownLatch

CountDownLatch一個或多個程等待其他程完成操作。

      假如有這樣一個需求:我需要解析一個Excel裏多個sheet的數據,此可以考使用多線程,每個程解析一個sheet裏的數據,等到所有的sheet都解析完之後,程序需要提示解析完 成。在個需求中,要實現程等待所有程完成sheet的解析操作,最簡單的做法是使用 join()方法。
 
 
1.使用 join()方法控制主線程最後執行
/**
 * sheet2 解析完成
 * sheet1 解析完成
 * sheet3 解析完成
 * 所有sheet解析完成
 */
public class JoinCountDownLatchTest {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 =new Thread(()->{
            System.out.println("sheet1 解析完成");
        });
        Thread t2 =new Thread(()->{
            System.out.println("sheet2 解析完成");
        });
        Thread t3 =new Thread(()->{
            System.out.println("sheet3 解析完成");
        });
        t1.start();
        t2.start();
        t3.start();
        t1.join();
        t2.join();
        t3.join();
        System.out.println("所有sheet解析完成");
    }
}

原理:join用於當前程等待join束。其實現原理是不停檢查join程是否存 活,如果join程存活則讓當前程永等待。

2.使用CountDownLatch併發工具類控制主線程最後執行

/**
 * Thread-0: 解析完成
 * Thread-2: 解析完成
 * Thread-1: 解析完成
 * 所有sheet解析完成
 */
public class CountDownLatchTest {
    public static void main(String[] args) throws InterruptedException {
        //1.計數器初始值爲3
        CountDownLatch countDownLatch=new CountDownLatch(3);

        for (int i = 0; i < 3; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+": 解析完成");
                try {
                    Thread.sleep(300);
                    //2.每次減一
                    countDownLatch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
        //3.await方法 會阻塞當前線程,直到N變成零
        countDownLatch.await();
        System.out.println("所有sheet解析完成");
    }
}

注意:數器必大於等於0,只是等於0候,數器就是零,調await方法不會 阻塞當前程。CountDownLatch不可能重新初始化或者修改CountDownLatch象的內部器的。一個調countDown方法happen-before,另外一個調await方法。

(二)同步屏障CyclicBarrier

        CyclicBarrier的字面意思是可循使用(Cyclic)的屏障(Barrier)。它要做的事情是,一組線程到達一個屏障(也可以叫同步點)被阻塞,直到最後一個程到達屏障,屏障纔會 ,所有被屏障截的程纔會繼續運行。
 
        CyclicBarrier的構造方法是CyclicBarrierint parties),其參數表示屏障截的程數量,每個調await方法告CyclicBarrier我已到達了屏障,然後當前程被阻塞。
 
1.CyclicBarrierint parties)構造方法的使用
public class CyclicBarrierTest {
    public static void main(String[] args) throws BrokenBarrierException, InterruptedException {
        //1.參數2表示屏障攔截的線程數量
        CyclicBarrier cyclicBarrier = new CyclicBarrier(2);
        new Thread(()->{
            try {
                //2.子線程調用await方法告訴訴CyclicBarrier我已經到達了屏障
                cyclicBarrier.await();
                System.out.println("sheet1");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }).start();
        //3.主線程調用await方法告訴訴CyclicBarrier我已經到達了屏障
        cyclicBarrier.await();
        System.out.println("sheet2");

    }
}

注意:

       一般是主線程優先於子線程執行,但在這裏程和子程的調度是由CPU決定的,兩個程都有可能先行,所以會生兩種出,第一種:sheet1、sheet2  第二種:sheet2、sheet1。

        如果把new CyclicBarrier(2)修改成new CyclicBarrier(3)程和子程會永等待,因沒有第三個await方法,即沒有第三個程到達屏障,所以之前到達屏障的兩個 程都不會繼續執行。
 
       CyclicBarrier提供一個更高的構造函數CyclicBarrierint partiesRunnable barrier-Action),用於在程到達屏障barrierAction,方便理更復業務場景。
 

2. CyclicBarrier的應用場景

       CyclicBarrier可以用於多算數據,最後合併果的景。例如,用一個Excel保存了用所有行流水,每個Sheet保存一個賬戶近一年的每筆行流水,在需要統計的日均行流水,先用多理每個sheet裏的行流水,都行完之後,得到每個sheet的日 行流水,最後,再用barrierAction程的果,算出整個Excel的日均行流 水。
 
public class BankWaterService implements Runnable {

    /*** 創建4個屏障,處理完之後執行當前類的run方法 */
    private CyclicBarrier c = new CyclicBarrier(4, this);
    /*** 假設只有4個sheet,所以只啓動4個線程 */
    private Executor executor = Executors.newFixedThreadPool(4);
    /*** 保存每個sheet計算出的銀流結果 */
    private ConcurrentHashMap<String, Integer> sheetBankWaterCount = new ConcurrentHashMap<String, Integer>();

    private void count(){
        for (int i = 0; i < 4; i++) {
            executor.execute(()->{
                sheetBankWaterCount.put(Thread.currentThread().getName(),1);
                try {
                    c.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            });
        }
    }

    //到達屏障攔截的線程數量,執行以下線程
    @Override
    public void run() {
        int result = 0;
        for(Map.Entry<String, Integer> sheet : sheetBankWaterCount.entrySet()){
            result = result+sheet.getValue();
        }
        System.out.println("result = " + result);
    }

    public static void main(String[] args) {
        BankWaterService bankWaterService = new BankWaterService();
        bankWaterService.count();
    }
}

3.CyclicBarrier和CountDownLatch的區別

  • CountDownLatch是線程組之間的等待,即一個(或多個)線程等待N個線程完成某件事情之後再執行;而CyclicBarrier則是線程組內的等待,即每個線程相互等待,即N個線程都被攔截之後,然後依次執行。
  • CountDownLatch是減計數方式,而CyclicBarrier是加計數方式。
  • CountDownLatch計數爲0無法重置,而CyclicBarrier計數達到初始值,則可以重置。
  • CountDownLatch不可以複用,而CyclicBarrier可以複用。

(三)控制併發線程數的Semaphore

Semaphore(信號量)是用來控制同時訪問特定源的程數量,它通過協調各個程,以保合理的使用公共源。
@Slf4j
public class SemaphoreTest {
    private final static int threadCount = 30;
    public static void main(String[] args) throws Exception {
        ExecutorService exec = Executors.newCachedThreadPool();
        //表示允 許3個線程獲取許可證,也就是最大併發數是3
        final Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < threadCount; i++) {
            final int threadNum = i;
            exec.execute(() -> {
                try {
                    // 獲取一個許可
                    semaphore.acquire();
                    //一次三個線程執行test方法
                    test(threadNum);
                    // 釋放一個許可
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
            });
        }
        exec.shutdown();
    }
    private static void test(int threadNum) throws Exception {
        log.info(Thread.currentThread().getName()+" is running");
        Thread.sleep(10000);
    }
}
在代中,然有30程在行,但是隻允許3個並發執行。Semaphore的構造方法Semaphoreint permits)接受一個整型的數字,表示可用的數量。Semaphore3)表示允 許3,也就是最大並數是3Semaphore的用法也很簡單,首先程使用 Semaphoreacquire()方法取一個,使用完之後調release()方法歸還許

 

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