併發容器中常用的同步組件

CountDownLatch

通過基數來保證線程是否需要一直阻塞。

public class CountDownLatchExample {
    private static int threadCount = 200;
    public static void main(String[] args) throws InterruptedException {
        ExecutorService exec = Executors.newCachedThreadPool();
        final CountDownLatch countDownLatch = new CountDownLatch(threadCount);
        for (int i=0; i<threadCount; i++) {
            final int threadNum = i;
            exec.execute(()->{
                try {
                    test(threadNum);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    countDownLatch.countDown();
                }
            });
        }
        countDownLatch.await();
        System.out.println("finish");
        exec.shutdown();
    }

    private static void test(int threadNum) throws InterruptedException {
	    Thread.sleep(10);
        System.out.println("thread number: " + threadNum);
    }
}
Semaphore

信號量,可以控制併發訪問的線程個數,控制某個資源被同時訪問的個數,常用於僅能提供有限訪問的資源。下面代碼中的任務,只有大約30個線程中的任務會被執行,其餘因爲等待超時被丟棄。

public class SemaphoreExample1 {
	// 併發線程數
   private static int threadCount = 200;
   public static void main(String[] args) {
   	// 線程池
       final ExecutorService exec = Executors.newCachedThreadPool();
       // 聲明信號量10個,表示控制併發訪問線程數爲10。
       final Semaphore semaphore = new Semaphore(10);
       for (int i=0; i<threadCount; i++) {
           final int threadNum = i;
           exec.execute(()->{
               try {
               		// 嘗試獲取信號量,獲取不到等待30ms,等待後獲取不到則丟棄任務
                   if(semaphore.tryAcquire(30, TimeUnit.MILLISECONDS)) {
                       test(threadNum);
                       // 釋放信號量
                       semaphore.release();
                   }
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           });
       }
       exec.shutdown();
   }

   private static void test(int threadNum) throws InterruptedException {
       System.out.println(Thread No.: " +threadNum);
       Thread.sleep(10);
   }
}
CyclicBarrier

多個線程互相等待。在涉及固定線程數、這些線程必須彼此等待時,使用CyclicBarrier。之所以成爲cyclic,是因爲它可以reset後重新計數。

public class CyclicBarrierExample3 {
    // 線程到達屏障時,優先執行runnable。
    private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5, ()->{
        System.out.println("callback is running.");
    });

    public static void main(String[] args) throws InterruptedException {
        ExecutorService exec = Executors.newCachedThreadPool();
        int threadCount = 10;
        for (int i=0; i<threadCount; i++) {
            final int threadNum = i;
            Thread.sleep(1000);
            exec.execute(()->{
                try {
                    race(threadNum);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            });
        }
        exec.shutdown();
    }

    private static void race(int threadNum) throws InterruptedException, BrokenBarrierException, TimeoutException {
        Thread.sleep(1000);
        System.out.println("Thread "+ threadNum + " is ready");
        cyclicBarrier.await();
        System.out.println("Thread "+ threadNum + " continue.");
    }
}
ReentrantLock

和synchronized具有類似的屬性和性能,但使用時比synchronize複雜,使用不當會造成死鎖。僅在需要ReentrantLock的獨有功能時,才推薦使用。
ReentrantLock的獨有功能:

  1. 可指定是公平鎖還是非公平鎖。
  2. 提供了一個Condition類,可以分組喚醒需要喚醒的線程。
  3. 提供能夠中斷等待鎖的線程的機制,lock.lockInterruptibly()。
Future

通過Thread和Runnable創建的線程,在執行完成後無法獲得執行結果。通過Callable、Future和FutureTask可以獲得線程執行結果。
使用Future獲取線程執行結果:

public class FutureExample {
    static class MyCallable implements Callable<String> {
        @Override
        public String call() throws InterruptedException {
            System.out.println("do something in MyCallable");
            Thread.sleep(5000);
            return "Done";
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService exec = Executors.newCachedThreadPool();
        Future<String> future = exec.submit(new MyCallable());
        System.out.println("do something in main");
        System.out.println("thread result: " + future.get());
    }
}

使用FutureTask獲取線程執行結果,更簡單:

public class FutureTaskExample {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService exec = Executors.newCachedThreadPool();
        FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                System.out.println("do something in myCallable");
                Thread.sleep(5000);
                return "Done";
            }
        });
        new Thread(futureTask).start();
        System.out.println("do something in main");
        System.out.println("thread result: " + futureTask.get());
    }
}
BlockingQueue

阻塞隊列提供了四套方法進行插入、刪除和檢測,對不能立刻執行會有不同的反應。

Type Throw Exception Special Value Blocks Times Out
Insert add(o) offer(o) put(o) offer(o, timeout, timeunit)
Remove remove(o) poll(o) take(o) poll(o, timeout, timeunit)
Insert element(o) peek(o)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章