1. 程式人生 > >多執行緒(十一): 計數器CountDownLatch和CyclicBarrier

多執行緒(十一): 計數器CountDownLatch和CyclicBarrier

public static void main(String[] args) {
    System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t\trunning...");
    Thread thread = new Thread(() -> {
        System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\trunning...");
        try {
            Thread.sleep(1000
); } catch (InterruptedException e) { } System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\tover"); })
; Thread thread2 = new Thread(() -> { System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\trunning..."); try
{ Thread.sleep(1000); } catch (InterruptedException e) { } System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\tover"); })
; thread.start(); thread2.start(); IntStream.range(0, 10).forEach(i -> { System.out.println(new
Date() + "\t" + Thread.currentThread().getName() + "\t\t i=" + i); })
; }

該示例主執行緒、Thread-0、Thread-1都是並行執行的
這裡寫圖片描述


讓main方法的最後的迴圈體放到最後執行

public static void main(String[] args) throws InterruptedException {
    System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t\trunning...");

    Thread threadA = new Thread(() -> {
        System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\trunning...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
        System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\tover");
    }, "Thread-A");

    Thread threadB = new Thread(() -> {
        try {
            // 先啟動後join
            threadA.start();
            // A join B 就是先執行A再執行B
            threadA.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\trunning...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
        System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\tover");
    }, "Thread-B");


    threadB.start();
    // B join mian, B先執行,然後再執行mian,
    // 當執行B的時候,發現A join B了,就先執行AA執行完,執行B,而Bjoin mian,所以main最後執行
    threadB.join();

    IntStream.range(0, 10).forEach(i -> {
        try { Thread.sleep(1000); } catch (InterruptedException e) { }
        System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t\t i=" + i);
    });
}

這裡寫圖片描述


public static void main(String[] args) throws InterruptedException {
    CountDownLatch countDownLatch = new CountDownLatch(2);

    System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t\trunning...");

    Runnable runnable = () -> {
        System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\trunning...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
        System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\tover");
        // 減一:線上程體內部呼叫
        countDownLatch.countDown();
    };

    // Thread-0 和 Thread-1並行執行
    new Thread(runnable).start();
    new Thread(runnable).start();

    // 但是隻有countDownLatch中的count=0時才會繼續往下執行,否則是阻塞的
    countDownLatch.await();

    IntStream.range(0, 10).forEach(i -> {
        System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t\t i=" + i);
    });
}

這裡寫圖片描述

join()和CountDownLatch

  • join也可以讓某個執行緒執行完之後再執行另一個執行緒,即讓執行緒之間有先後順序,一般是讓某個子執行緒加入父執行緒,兩個執行緒之間有父子關係,join會釋放鎖

  • CountDownLatch是一種計數器,多個執行緒之間沒有父子關係是平級的,當計數器為0的時候才能往下執行

  • 具體使用哪個要看執行緒之間是否有父子關係

CyclicBarrier: 用於控制所有執行緒的前面部分程式碼都完成時才能執行所有執行緒後部分的程式碼

package java.util.concurrent;

public class CyclicBarrier {
    // 
    private final int parties;

    public CyclicBarrier(int parties);

    // 每執行一次等待方法,計數器就減1,計數器大於0之前等待方法就會阻塞暫停後面的程式碼的執行
    // 當計數器為0,所有執行緒的等待後面的程式碼併發執行

    public int await() throws InterruptedException, BrokenBarrierException;
}
public static void main(String[] args) throws InterruptedException {
    System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t\trunning...");

    CyclicBarrier cyclicBarrier = new CyclicBarrier(5);

    Runnable runnable = () -> {
        System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\trunning...");
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
        }

        try { cyclicBarrier.await(); } catch (Exception e) { }
        System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\tover");
    };


    IntStream.range(0, 5).forEach(i -> {
        new Thread(runnable).start();
    });
}

這裡寫圖片描述

CountDownLatch與CyclicBarrier比較

  • CountDownLatch: 計數器自己可以在任意地方減1 countDown(), await()方法線上程外呼叫,當計數器為0的時候才可以執行執行緒外的後面的程式碼
    • CyclicBarrier:線上程內呼叫await(),計數器會自動減1,用於控制執行緒內的後面的程式碼的執行
    • CountDownLatch用來控制執行緒外的程式碼,用於阻塞父執行緒的程式碼,CyclicBarrier用於控制執行緒內的程式碼,用於阻塞當前執行緒的程式碼