閉鎖,信號量,柵欄

1.   閉鎖(countDownLatch)

1.1.     做用:

至關於一扇門,在閉鎖到達結束狀態以前,這扇門是關着的,因此的線程都不容許經過,當閉鎖到達結束狀態,這扇門打開並容許全部的線程經過。在閉鎖達到結束狀態後,將不會再改變狀態,這扇門永遠處於打開狀態。算法

1.2.     閉鎖使用場景

1) 確保某個計算在其全部資源都被初始化以後才繼續執行;多線程

2) 確保某個服務在其全部所依賴的服務都已經啓動後在啓動;jvm

3) 等待某個操做的全部參與者都就緒再繼續執行(例如王者榮耀匹配成功後需等待全部玩家確認後才能進行選英雄)。ide

1.3.     閉鎖的簡單使用

CountDownLatch是一種靈活的閉鎖實現,上述場景均可以都可使用。該類的簡單用法以下:函數

public class SysUtil {

   final CountDownLatch startGate;

   final CountDownLatch endGate;


   public static void main(String[] args) throws InterruptedException {


      int num = 20;

      SysUtil sysUtil = new SysUtil(1, num);

      for (int i = 0; i < num; i++) {

         Thread thread = new Thread(sysUtil.new Worker());

         thread.start();

      }


      long time = System.currentTimeMillis();

      System.out.println("準備好所需的資源");

      // doPreparedResource()


      // open the door

      sysUtil.startGate.countDown();


      try {

         // 等待全部線程執行完成

         sysUtil.endGate.await();

      } catch (InterruptedException e) {

         e.printStackTrace();

      }

      long endTime = System.currentTimeMillis();

      System.out.println(endTime - time);

   }


   public SysUtil(int start, int end) {

      startGate = new CountDownLatch(start);

      endGate = new CountDownLatch(end);

   }


   class Worker implements Runnable{


      private final CountDownLatch startSignal;

      private final CountDownLatch doSignal;


      public Worker() {

         this.startSignal = startGate;

         this.doSignal = endGate;

      }


      @Override

      public void run() {

         try {

            startSignal.await();

         } catch (InterruptedException e) {

            e.printStackTrace();
         }
         System.out.println(Thread.currentThread().getName() +

                ": i through the startGate");
         doSignal.countDown();

      }

   }

}
View Code

上面代碼使用了兩個閉鎖,分別表示「起始門」和「結束門」,起始門初始值設置爲1,結束門設置爲工做線程數量。在資源未完成加載以前(doPreparedResource()),全部線程被拒之起始門外,加載完資源後,大門打開,線程進入辦公樓開始幹活,等到下班時間了,辦公大樓就能夠打開結束門放線程回家。測試

 

2.   FutureTask

2.1.     簡介

FutureTask實現了Future語義,表示一直抽象的可生成結果的計算。FutureTask表示的計算是經過Callable來實現的。計算結果經過get()方法得到,若是任務已經完成,那麼get方法會當即返回結果,不然get方法將阻塞直到任務進入完成狀態(包括正常結束、因爲取消而結束及因爲異常而結束),而後返回結果或拋出異常。ui

public class FutureTask<V> implements RunnableFuture<V>this

public interface RunnableFuture<V> extends Runnable, Future<V>spa

Runable接口咱們熟悉只有一個run()方法線程

Future接口方法以下:

2.2.     FutureTask的簡單使用

public class FutureTaskTest {

 

   @Test

   public void test() throws InterruptedException, ExecutionException {

      FutureTask<Integer> futureTask = new FutureTask<>(new Task());

      Thread thread = new Thread(futureTask);

      long startTime = System.currentTimeMillis();

      thread.start();

      System.out.println("result: " + futureTask.get());

      long endTime = System.currentTimeMillis();

      System.out.println("花費時間:" + (endTime - startTime));

   }

  

   class Task implements Callable<Integer>{

 

      @Override

      public Integer call() throws Exception {

         System.out.println("--------線程執行中------------");

         int sum = 0;

         for (int i = 0; i < 100; i++) {

            Thread.sleep(10);

            sum += i;

         }

         return sum;

      }

   }

}
View Code

3.   信號量(Semaphore)

3.1.     簡介

計數信號量用來控制同時訪問某個特定資源的操做數量,或者同時執行某個指定操做的數量。另外信號量還能夠用來實現某種資源池,或對容器施加邊界。

Semaphore中管理着一組虛擬許可(permit),許可的初始數量可經過構造函數來指定,在執行操做以前首先得到(acquire)許可,在使用後釋放許可。若是沒有許可,那麼acquire將阻塞直到有permit(或者直到被中斷或操做超時)。Release方法將返回一個許可給信號量。

3.2.     簡單使用

/*

 * 利用信號量對set設置邊界

 */

public class SemephoreTest<T> {

  

   private final Set<T> set;

   private final Semaphore semaphore;

  

   public SemephoreTest(int boundNum) {

  

      this.set = Collections.synchronizedSet(new HashSet<>());

      // 建立必定數量的許可

      this.semaphore = new Semaphore(boundNum);

   }

  

   public boolean add(T o) {

      boolean wadAdd = false;

      try {

         // 請求許可

         semaphore.acquire();

        

         wadAdd = set.add(o);

         return wadAdd;

      } catch (InterruptedException e) {

         e.printStackTrace();

      }finally {

         // 元素添加失敗,釋放許可

         if (!wadAdd) {

            semaphore.release();

         }

      }

     

      return wadAdd;

   }

  

   public boolean remove(T o) {

     

      boolean wasRemoved = set.remove(o);

      if (wasRemoved) {

         semaphore.release();

      }

      return wasRemoved;

   }

}
View Code

 

4.   柵欄(barrier)

4.1.     簡介

柵欄相似於閉鎖,它能阻塞一組線程直到某個事件發生。柵欄和閉鎖的區別在於,全部線程必須都到達柵欄位置了,才能繼續執行。閉鎖用於等待事件,而柵欄用於等待其餘線程。

CyclicBarrier可使必定的數量的參與方反覆地在柵欄處聚集,在並行迭代算法中很是有用。但線程到達柵欄處時將調用柵欄的await方法,該方法會阻塞當前線程直到全部的線程都到達了柵欄處。若是全部的線程都到達了柵欄處,柵欄將會打開,此時全部的線程都會被釋放,而柵欄將被重置以便下次使用(閉鎖不可被重置)。若是await調用超時或者阻塞的線程被中斷,全部的await調用都終止並拋出BrokenBarrierException。若是成功經過柵欄,await將爲每一個線程返回惟一的到達索引號。

4.2.     簡單使用

public class BarrierTest {

 

   private final CyclicBarrier cyclicBarrier;

   private Worker[] workers;

  

   public BarrierTest() {

      int count = Runtime.getRuntime().availableProcessors() * 2;

      count = 10;

      cyclicBarrier = new CyclicBarrier(count, new Runnable() {

        

         @Override

         public void run() {

            System.out.println("open the barrier");

           

         }

      });

     

      this.workers = new Worker[count];

      for (int i = 0; i < workers.length; i++) {

         workers[i] = new Worker();

      }

   }

  

   class Worker implements Runnable{

 

      @Override

      public void run() {

         System.out.println(Thread.currentThread().getName() + ":我到了,等人齊");

         try {

            cyclicBarrier.await();

         } catch (InterruptedException e) {

            e.printStackTrace();

         } catch (BrokenBarrierException e) {

            e.printStackTrace();

         }

         System.out.println(Thread.currentThread().getName() + "衝啊");

      }

   }

  

   public void start() {

      for (int i = 0; i < workers.length; i++) {

         new Thread(workers[i]).start();

      }

   }

  

   // 不可以使用Junit進行多線程的測試,Junit中是將當前 test做爲參數,放入到 Junit TestRunner中的main函數

   // 做爲一個子線程運行,當測試線程運行成功,將把jvm中止。具體看下面代碼

   /*

    

   public static void main(String args[]) {

       TestRunner aTestRunner = new TestRunner();

       try {

          TestResult r = aTestRunner.start(args);

          if (!r.wasSuccessful())

              System.exit(FAILURE_EXIT);

          System.exit(SUCCESS_EXIT);

       } catch (Exception e) {

          System.err.println(e.getMessage());

          System.exit(EXCEPTION_EXIT);

       }

   }

    */

   @Test

   public void test() {

      new BarrierTest().start();

   }

  

   public static void main(String[] args) {

      new BarrierTest().start();

   }

}
View Code

5.   Exchanger(兩方柵欄)

5.1.     簡介

Exchanger類可用於兩個線程之間交換信息,是一種兩方柵欄。可簡單地將Exchanger對象理解爲一個包含兩個格子的容器,經過exchanger方法能夠向兩個格子中填充信息。當兩個格子中的均被填充時,該對象會自動將兩個格子的信息交換,而後返回給線程,從而實現兩個線程的信息交換。

另外須要注意的是,Exchanger類僅可用做兩個線程的信息交換,當超過兩個線程調用同一個exchanger對象時,獲得的結果是隨機的,未獲得配對的線程,則會被阻塞,永久等待,直到與之配對的線程到達位置,exchanger對象僅關心其包含的兩個「格子」是否已被填充數據,當兩個格子都填充數據完成時,該對象就認爲線程之間已經配對成功,而後開始執行數據交換操做。

5.2.     簡單使用

public class ExchangerTest {

 

   public static void main(String[] args) {

      Exchanger<String> barrier = new Exchanger<>();

      Comsumer comsumer = new ExchangerTest(). new Comsumer(barrier);

      Producer prodecer = new ExchangerTest(). new Producer(barrier);

      Thread thread1 = new Thread(comsumer);

      Thread thread2 = new Thread(prodecer);

      thread1.start();

      thread2.start();

   }

  

   class Comsumer implements Runnable{

 

      private Exchanger<String> changer;

     

      public Comsumer(Exchanger<String> exchanger) {

         this.changer = exchanger;

      }

      @Override

      public void run() {

         String changes = null;

         try {

            changes = changer.exchange("money");

         } catch (InterruptedException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

         }

         System.out.println("Consumer recieve:" + changes);

        

      }

   }

  

   class Producer implements Runnable{

 

      private Exchanger<String> changer;

     

      public Producer(Exchanger<String> exchanger) {

         this.changer = exchanger;

      }

      @Override

      public void run() {

         String changes = null;

         try {

            changes = changer.exchange("products");

         } catch (InterruptedException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

         }

         System.out.println("Producer recieve:" + changes);


      }

   }

}
View Code
相關文章
相關標籤/搜索