《java併發程式設計的藝術》併發工具類

sayWhat_sayHello發表於2018-07-21

CountDownLatch

join:

public class CountDownLatchTest {
    public static void main(String[] args) throws InterruptedException {
        Thread parser1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("parser1 finish");
            }
        });

        Thread parser2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("parser2 finish");
            }
        });

        parser1.start();
        parser2.start();
        parser1.join();
        parser1.join();
        System.out.println("all parser finish");
    }
}

countDownLatch:

import java.util.concurrent.CountDownLatch;

public class CountDownLatchTest {

    static CountDownLatch c = new CountDownLatch(2);

    public static void main(String[] args) throws InterruptedException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(1);
                c.countDown();
                System.out.println(2);
                c.countDown();
            }
        }).start();

        c.await();
        System.out.println(3);
    }
}

建構函式接收一個int型別的引數作為計數器,該引數作為等待的次數。

當我們呼叫countDown方法時,該引數減1,CountDownLatch的await方法會阻塞當前執行緒,直到引數為0.
await有一個帶指定時間的方法:await(long time,TimeUnit unit);

CyclicBarrier

CyclicBarrier的字面意思是可迴圈的屏障。讓一組執行緒到達一個屏障時被阻塞,直到最後一個執行緒到達屏障時,屏障才會開門,所有被屏障攔截的執行緒才會繼續執行。

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierTest {
    static CyclicBarrier c = new CyclicBarrier(2);
    public static void main(String[] args){
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    c.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
                System.out.println(1);
            }
        });

        thread.start();

        try {
            c.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
        System.out.println(2);

    }
}

CyclicBarrier預設的構造方法是CyclicBarrier(int partier),其參數列示屏障攔截的執行緒數量,每個執行緒呼叫await方法告訴CyclicBarrier我已經到達屏障了,然後當前執行緒被阻塞。直到最後一個執行緒到達屏障時,屏障才會開門,所有被屏障攔截的執行緒才會繼續執行。

CyclicBarrier還有一個建構函式CyclicBarrier(int parties,Runnable barrierAction),用於執行緒到達屏障時,優先執行barrierAction。

static CyclicBarrier c = new CyclicBarrier(5, new Before());

    public static void main(String[] args) {

        Runnable r = () -> {
            System.out.println("i am a thread");
            try {
                c.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        };

        Thread[] threads = new Thread[4];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(r);
            threads[i].start();
        }

        try {
            c.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
        System.out.println("over");
    }

    static class Before implements Runnable {

        @Override
        public void run() {
            System.out.println("i am out of barrier");
        }
    }

執行結果:

i am a thread
i am a thread
i am a thread
i am a thread
i am out of barrier
over

CyclicBarrier的計數器可以使用reset()方法重置。

Semaphore

Semaphore訊號量用來控制同時訪問特定資源的執行緒數量,它通過協調各個執行緒,保證合理的使用公共資源。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class SemaphoreTest {
    public static final int THREAD_COUNT = 30;
    private static ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_COUNT);
    private static Semaphore s = new Semaphore(10);

    public static void main(String[] args){
        for (int i = 0; i < THREAD_COUNT; i++) {
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        s.acquire();
                        System.out.println("save data");
                        s.release();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        threadPool.shutdown();
    }
}

訊號量可以用來控制併發量。首先執行緒使用Semaphore的acquire()方法獲得一個許可證,使用完後呼叫release()方法歸還許可證,擁有許可證的執行緒才可以執行。

Exchanger

Exchanger是一個執行緒間協作的工具類。提供了一個同步點,在這個同步點,兩個執行緒可以交換彼此的資料。當第一個執行緒執行exchange()方法,它會一直等待第二個執行緒也執行exchange()方法,當兩個執行緒都到達同步點時,執行緒可以交換資料。

Exchanger可以用於遺傳演算法,遺傳演算法需要選出兩個人作為交配物件,這時候交換兩個人的資料,並使用交叉規則得出2個交配結果。Exchanger也可以用於校對工作。

import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExchangerTest {
    private static final Exchanger<String> ex = new Exchanger<>();
    private static ExecutorService threadPool = Executors.newFixedThreadPool(2);
    public static void main(String[] args){
        threadPool.execute(()->{
            String A = "銀行流水A";
            try {
                ex.exchange(A);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        threadPool.execute(()->{
            String B = "銀行流水B";
            try {
                String A = ex.exchange("B");
                System.out.println("A錄入:"+A);
                System.out.println("B錄入:"+B);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });

        threadPool.shutdown();
    }
}

相關文章