Java並發包之閉鎖/柵欄/信號量(轉)


本文轉自http://blog.csdn.net/u010942020/article/details/79352560 感謝作者
一、Java多線程總結:
  1. 描述線程的類:Runable和Thread都屬於java.lang包。
  2. 內置鎖synchronized屬於jvm關鍵字,內置條件隊列操作接口Object.wait()/notify()/notifyAll()屬於java.lang包。
  3. 提供內存可見性和防止指令重排的volatile屬於jvm關鍵字。
  4. 而java.util.concurrent包(J.U.C)中包含的是java並發編程中有用的一些工具類,包括幾個部分: 
    • locks部分:包含在java.util.concurrent.locks包中,提供顯式鎖(互斥鎖和速寫鎖)相關功能。
    • atomic部分:包含在java.util.concurrent.atomic包中,提供原子變量類相關的功能,是構建非阻塞算法的基礎。
    • executor部分:散落在java.util.concurrent包中,提供線程池相關的功能。
    • collections部分:散落在java.util.concurrent包中,提供並發容器相關功能。
    • tools部分:散落在java.util.concurrent包中,提供同步工具類,如信號量、閉鎖、柵欄等功能。 
      這里寫圖片描述
二、同步工具類詳解

1、Semaphore信號量:跟鎖機制存在一定的相似性,semaphore也是一種鎖機制,所不同的是,reentrantLock是只允許一個線程獲得鎖,而信號量持有多個許可(permits),允許多個線程獲得許可並執行。可以用來控制同時訪問某個特定資源的操作數量,或者同時執行某個指定操作的數量。

示例代碼:

 5 public class TIJ_semaphore { 6 public static void main(String[] args) { 7 ExecutorService exec = Executors.newCachedThreadPool(); 8 final Semaphore semp = new Semaphore(5); // 5 permits 9 10 for (int index = 0; index < 20; index++) { 11 final int NO = index; 12 Runnable run = new Runnable() { 13 public void run() { 14 try { // if 1 permit avaliable, thread will get a permits and go; if no permit avaliable, thread will block until 1 avaliable 15 semp.acquire(); 16 System.out.println("Accessing: " + NO); 17 Thread.sleep((long) (10000); 18 semp.release(); 19 } catch (InterruptedException e) { 20 } 21 } 22 }; 23 exec.execute(run); 24 } 25 exec.shutdown(); 26 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

2、CountDownLatch閉鎖:允許一個或多個線程一直等待,直到其他線程的操作執行完后再執行。CountDownLatch是通過一個計數器來實現的,計數器的初始值為線程的數量。每當一個線程完成了自己的任務后,計數器的值就會減1。當計數器值到達0時,它表示所有的線程已經完成了任務,然后在閉鎖上等待的線程就可以恢復執行任務。

主要方法: 
1. CountDownLatch.await():將某個線程阻塞住,直到計數器count=0才恢復執行。 
2. CountDownLatch.countDown():將計數器count減1。

使用場景: 
1. 實現最大的並行性:有時我們想同時啟動多個線程,實現最大程度的並行性。例如,我們想測試一個單例類。如果我們創建一個初始計數為1的CountDownLatch,並讓所有線程都在這個鎖上等待,那么我們可以很輕松地完成測試。我們只需調用 一次countDown()方法就可以讓所有的等待線程同時恢復執行。 
2. 開始執行前等待n個線程完成各自任務:例如應用程序啟動類要確保在處理用戶請求前,所有N個外部系統已經啟動和運行了。 
3. 死鎖檢測:一個非常方便的使用場景是,你可以使用n個線程訪問共享資源,在每次測試階段的線程數目是不同的,並嘗試產生死鎖。 
4. 計算並發執行某個任務的耗時。

示例代碼:

public class CountDownLatchTest { public void timeTasks(int nThreads, final Runnable task) throws InterruptedException{ final CountDownLatch startGate = new CountDownLatch(1); final CountDownLatch endGate = new CountDownLatch(nThreads); for(int i = 0; i < nThreads; i++){ Thread t = new Thread(){ public void run(){ try{ startGate.await(); try{ task.run(); }finally{ endGate.countDown(); } }catch(InterruptedException ignored){ } } }; t.start(); } long start = System.nanoTime(); System.out.println("打開閉鎖"); startGate.countDown(); endGate.await(); long end = System.nanoTime(); System.out.println("閉鎖退出,共耗時" + (end-start)); } public static void main(String[] args) throws InterruptedException{ CountDownLatchTest test = new CountDownLatchTest(); test.timeTasks(5, test.new RunnableTask()); } class RunnableTask implements Runnable{ @Override public void run() { System.out.println("當前線程為:" + Thread.currentThread().getName()); } } 執行結果為: 打開閉鎖 當前線程為:Thread-0 當前線程為:Thread-3 當前線程為:Thread-2 當前線程為:Thread-4 當前線程為:Thread-1 閉鎖退出,共耗時1109195 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

3、CyclicBarrier柵欄:用於阻塞一組線程直到某個事件發生。所有線程必須同時到達柵欄位置才能繼續執行下一步操作,且能夠被重置以達到重復利用。而閉鎖是一次性對象,一旦進入終止狀態,就不能被重置。

示例代碼:

public class CyclicBarrierTest { private final CyclicBarrier barrier; private final Worker[] workers; public CyclicBarrierTest(){ int count = Runtime.getRuntime().availableProcessors(); this.barrier = new CyclicBarrier(count, new Runnable(){ @Override public void run() { System.out.println("所有線程均到達柵欄位置,開始下一輪計算"); } }); this.workers = new Worker[count]; for(int i = 0; i< count;i++){ workers[i] = new Worker(i); } } private class Worker implements Runnable{ int i; public Worker(int i){ this.i = i; } @Override public void run() { for(int index = 1; index < 3;index++){ System.out.println("線程" + i + "第" + index + "次到達柵欄位置,等待其他線程到達"); try { //注意是await,而不是wait barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); return; } catch (BrokenBarrierException e) { e.printStackTrace(); return; } } } } public void start(){ for(int i=0;i<workers.length;i++){ new Thread(workers[i]).start(); } } public static void main(String[] args){ new CyclicBarrierTest().start(); } } 執行結果為: 線程0第1次到達柵欄位置,等待其他線程到達 線程1第1次到達柵欄位置,等待其他線程到達 線程2第1次到達柵欄位置,等待其他線程到達 線程3第1次到達柵欄位置,等待其他線程到達 所有線程均到達柵欄位置,開始下一輪計算 線程3第2次到達柵欄位置,等待其他線程到達 線程2第2次到達柵欄位置,等待其他線程到達 線程0第2次到達柵欄位置,等待其他線程到達 線程1第2次到達柵欄位置,等待其他線程到達 所有線程


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM