1. 程式人生 > >(七) Java多執行緒詳解之常用執行緒同步工具類

(七) Java多執行緒詳解之常用執行緒同步工具類

執行緒同步工具類

訊號燈(Semaphore)

可以維護當前訪問自身的執行緒個數並提供了同步機制,使用Semaphore可以控制同時訪問資源的執行緒個數,示例程式碼如下:

public class ThreadExample16 {
    public static void main(String[] args) {
        ExecutorService service = Executors.newCachedThreadPool();
        final Semaphore sp = new Semaphore(3);
        for (int i = 0
; i < 10; i++) { Runnable runnable = new Runnable() { public void run() { try { sp.acquire(); } catch (InterruptedException e) { e.printStackTrace(); } System.out
.println("執行緒" + Thread.currentThread().getName() + "進入,當前已有" + (3 - sp.availablePermits()) + "個併發"); try { Thread.sleep((long) (Math.random() * 10000)); } catch (InterruptedException e) { e.printStackTrace(); } System.out
.println("執行緒" + Thread.currentThread().getName() + "即將離開"); sp.release(); // 下面程式碼有時候執行不準確,因為其沒有和上面的程式碼合成原子單元 System.out.println("執行緒" + Thread.currentThread().getName() + "已離開,當前已有" + (3 - sp.availablePermits()) + "個併發"); } }; service.execute(runnable); } } }

該段程式碼通過訊號燈保證同時執行的執行緒只能有三個

障礙器(CyclicBarrier)

可以實現多個執行緒執行完畢後再執行某段邏輯,例如一個部門出去聚餐要等待所有人都到齊後再一起出發,以下為示例程式碼:

public class ThreadExample17 {
    public static void main(String[] args) {
        ExecutorService service = Executors.newCachedThreadPool();
        final CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
        for (int i = 0; i < 3; i++) {
            Runnable runnable = new Runnable() {
                public void run() {
                    try {
                        Thread.sleep((long) (Math.random() * 10000));
                        System.out.println("執行緒" + Thread.currentThread().getName() + "即將到達集合地點1,當前已有" + (cyclicBarrier.getNumberWaiting() + 1) + "個已經到達," + (cyclicBarrier.getNumberWaiting() == 2 ? "都到齊了,繼續走啊" : "正在等候"));
                        cyclicBarrier.await();

                        Thread.sleep((long) (Math.random() * 10000));
                        System.out.println("執行緒" + Thread.currentThread().getName() + "即將到達集合地點2,當前已有" + (cyclicBarrier.getNumberWaiting() + 1) + "個已經到達," + (cyclicBarrier.getNumberWaiting() == 2 ? "都到齊了,繼續走啊" : "正在等候"));
                        cyclicBarrier.await();

                        Thread.sleep((long) (Math.random() * 10000));
                        System.out.println("執行緒" + Thread.currentThread().getName() + "即將到達集合地點3,當前已有" + (cyclicBarrier.getNumberWaiting() + 1) + "個已經到達," + (cyclicBarrier.getNumberWaiting() == 2 ? "都到齊了,繼續走啊" : "正在等候"));
                        cyclicBarrier.await();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
            service.execute(runnable);
        }
        service.shutdown();
    }
}

同步計數器(CountDownLatch)

猶如倒計時計數器呼叫CountDownLatch物件的countDown方法就將計數器減1,當計數到達0時則所有等待者或單個等待者開始執行,示例程式碼如下:

public class ThreadExample18 {
    public static void main(String[] args) {
        ExecutorService service = Executors.newCachedThreadPool();
        final CountDownLatch cdOrder = new CountDownLatch(1);
        final CountDownLatch cdAnswer = new CountDownLatch(3);
        for (int i = 0; i < 3; i++) {
            Runnable runnable = new Runnable() {
                public void run() {
                    try {
                        System.out.println("執行緒" + Thread.currentThread().getName() + "正準備接受命令");
                        cdOrder.await();
                        System.out.println("執行緒" + Thread.currentThread().getName() + "已接受命令");
                        Thread.sleep((long) (Math.random() * 10000));
                        System.out.println("執行緒" + Thread.currentThread().getName() + "迴應命令處理結果");
                        cdAnswer.countDown();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
            service.execute(runnable);
        }
        try {
            Thread.sleep((long) (Math.random() * 10000));
            System.out.println("執行緒" + Thread.currentThread().getName() + "即將釋出命令");
            cdOrder.countDown();
            System.out.println("執行緒" + Thread.currentThread().getName() + "已傳送命令,正在等待結果");
            cdAnswer.await();
            System.out.println("執行緒" + Thread.currentThread().getName() + "已收到所有響應結果");
        } catch (Exception e) {
            e.printStackTrace();
        }
        service.shutdown();
    }
}

執行緒間資料交換(Exchanger)

用於實現兩個執行緒之間的資料交換,每個執行緒在完成一定的事務後想與對方交換資料,第一個先拿出資料的執行緒將一直等待第二個執行緒拿著資料到來時才能彼此交換資料,示例程式碼如下:

public class ThreadExample19 {
    public static void main(String[] args) {
        ExecutorService service = Executors.newCachedThreadPool();
        final Exchanger exchanger = new Exchanger();
        service.execute(new Runnable() {
            public void run() {
                try {
                    String data1 = "執行緒一的資料";
                    System.out.println("執行緒" + Thread.currentThread().getName() + "正在把資料" + data1 + "換出去");
                    Thread.sleep((long) (Math.random() * 10000));
                    String data2 = (String) exchanger.exchange(data1);
                    System.out.println("執行緒" + Thread.currentThread().getName() + "換回的資料為" + data2);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        service.execute(new Runnable() {
            public void run() {
                try {
                    String data1 = "執行緒二的資料";
                    System.out.println("執行緒" + Thread.currentThread().getName() + "正在把資料" + data1 + "換出去");
                    Thread.sleep((long) (Math.random() * 10000));
                    String data2 = (String) exchanger.exchange(data1);
                    System.out.println("執行緒" + Thread.currentThread().getName() + "換回的資料為" + data2);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

以上幾種工具類在適當的場景下會給開發帶來很大的便利,平時不需要記得具體怎麼執行只需要在遇到具體需求時知道有這麼個解決方案