CyclicBarrier的工作原理及其實例


 

        CyclicBarrier是多線程中一個重要的類,主要用於線程組內部之間的線程的相互等待問題。

 1.CyclicBarrier的工作原理      

        CyclicBarrier大致是可循環利用的屏障,顧名思義,這個名字也將這個類的特點給明確地表示出來了。首先,便是可重復利用,說明該類創建的對象可以復用;其次,屏障則體現了該類的原理:每個線程執行時,都會碰到一個屏障,直到所有線程執行結束,然后屏障便會打開,使所有線程繼續往下執行。

        這里介紹CyclicBarrier的兩個構造函數:CyclicBarrier(int parties)和CyclicBarrier(int parties, Runnable barrierAction) :前者只需要聲明需要攔截的線程數即可,而后者還需要定義一個等待所有線程到達屏障優先執行的Runnable對象。

        實現原理:在CyclicBarrier的內部定義了一個Lock對象,每當一個線程調用await方法時,將攔截的線程數減1,然后判斷剩余攔截數是否為初始值parties,如果不是,進入Lock對象的條件隊列等待。如果是,執行barrierAction對象的Runnable方法,然后將鎖的條件隊列中的所有線程放入鎖等待隊列中,這些線程會依次的獲取鎖、釋放鎖。

        舉例說明:如果一個寢室四個人約好了去球場打球,由於四個人准備工作不同,所以約好在樓下集合,並且四個人集合好之后一起出發去球場。

  1.  
    package concurrent;
  2.  
    import java.util.concurrent.CyclicBarrier;
  3.  
    import java.util.concurrent.*;
  4.  
    import java.util.concurrent.LinkedBlockingQueue;
  5.  
    import java.util.concurrent.ThreadPoolExecutor;
  6.  
    import java.util.concurrent.TimeUnit;
  7.  
    import java.util.*;
  8.  
    public class CyclicBarrierDemo {
  9.  
    private static final ThreadPoolExecutor threadPool=new ThreadPoolExecutor(4,10,60,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
  10.  
    //當攔截線程數達到4時,便優先執行barrierAction,然后再執行被攔截的線程。
  11.  
    private static final CyclicBarrier cb=new CyclicBarrier(4,new Runnable() {
  12.  
    public void run()
  13.  
    {
  14.  
    System.out.println( "寢室四兄弟一起出發去球場");
  15.  
    }
  16.  
    });
  17.  
    private static class GoThread extends Thread{
  18.  
    private final String name;
  19.  
    public GoThread(String name)
  20.  
    {
  21.  
    this.name=name;
  22.  
    }
  23.  
    public void run()
  24.  
    {
  25.  
    System.out.println(name+ "開始從宿舍出發");
  26.  
    try {
  27.  
    Thread.sleep( 1000);
  28.  
    cb.await(); //攔截線程
  29.  
    System.out.println(name+ "從樓底下出發");
  30.  
    Thread.sleep( 1000);
  31.  
    System.out.println(name+ "到達操場");
  32.  
     
  33.  
    }
  34.  
    catch(InterruptedException e)
  35.  
    {
  36.  
    e.printStackTrace();
  37.  
    }
  38.  
    catch(BrokenBarrierException e)
  39.  
    {
  40.  
    e.printStackTrace();
  41.  
    }
  42.  
    }
  43.  
    }
  44.  
    public static void main(String[] args) {
  45.  
    // TODO Auto-generated method stub
  46.  
    String[] str= { "李明","王強","劉凱","趙傑"};
  47.  
    for(int i=0;i<4;i++)
  48.  
    {
  49.  
    threadPool.execute( new GoThread(str[i]));
  50.  
    }
  51.  
    try
  52.  
    {
  53.  
    Thread.sleep( 4000);
  54.  
    System.out.println( "四個人一起到達球場,現在開始打球");
  55.  
    }
  56.  
    catch(InterruptedException e)
  57.  
    {
  58.  
    e.printStackTrace();
  59.  
    }
  60.  
     
  61.  
     
  62.  
    }
  63.  
     
  64.  
    }

運行程序,得到如下結果:

  1.  
    李明開始從宿舍出發
  2.  
    趙傑開始從宿舍出發
  3.  
    王強開始從宿舍出發
  4.  
    劉凱開始從宿舍出發
  5.  
    寢室四兄弟一起出發去球場
  6.  
    趙傑從樓底下出發
  7.  
    李明從樓底下出發
  8.  
    劉凱從樓底下出發
  9.  
    王強從樓底下出發
  10.  
    趙傑到達操場
  11.  
    王強到達操場
  12.  
    李明到達操場
  13.  
    劉凱到達操場
  14.  
    四個人一起到達球場,現在開始打球

        以上便是CyclicBarrier使用實例,通過await()方法對線程的攔截,攔截數加1,當攔截數為初始的parties,首先執行了barrierAction,然后對攔截的線程隊列依次進行獲取鎖釋放鎖。接下來,在這個例子上講解CyclicBarrier對象的復用特性。

  1.  
    package concurrent;
  2.  
    import java.util.concurrent.CyclicBarrier;
  3.  
    import java.util.concurrent.*;
  4.  
    import java.util.concurrent.LinkedBlockingQueue;
  5.  
    import java.util.concurrent.ThreadPoolExecutor;
  6.  
    import java.util.concurrent.TimeUnit;
  7.  
    import java.util.*;
  8.  
    public class CyclicBarrierDemo {
  9.  
    private static final ThreadPoolExecutor threadPool=new ThreadPoolExecutor(4,10,60,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
  10.  
    //當攔截線程數達到4時,便優先執行barrierAction,然后再執行被攔截的線程。
  11.  
    private static final CyclicBarrier cb=new CyclicBarrier(4,new Runnable() {
  12.  
    public void run()
  13.  
    {
  14.  
    System.out.println( "寢室四兄弟一起出發去球場");
  15.  
    }
  16.  
    });
  17.  
    private static class GoThread extends Thread{
  18.  
    private final String name;
  19.  
    public GoThread(String name)
  20.  
    {
  21.  
    this.name=name;
  22.  
    }
  23.  
    public void run()
  24.  
    {
  25.  
    System.out.println(name+ "開始從宿舍出發");
  26.  
    try {
  27.  
    Thread.sleep( 1000);
  28.  
    cb.await(); //攔截線程
  29.  
    System.out.println(name+ "從樓底下出發");
  30.  
    Thread.sleep( 1000);
  31.  
    System.out.println(name+ "到達操場");
  32.  
     
  33.  
    }
  34.  
    catch(InterruptedException e)
  35.  
    {
  36.  
    e.printStackTrace();
  37.  
    }
  38.  
    catch(BrokenBarrierException e)
  39.  
    {
  40.  
    e.printStackTrace();
  41.  
    }
  42.  
    }
  43.  
    }
  44.  
    public static void main(String[] args) {
  45.  
    // TODO Auto-generated method stub
  46.  
    String[] str= { "李明","王強","劉凱","趙傑"};
  47.  
    String[] str1= { "王二","洪光","雷兵","趙三"};
  48.  
    for(int i=0;i<4;i++)
  49.  
    {
  50.  
    threadPool.execute( new GoThread(str[i]));
  51.  
    }
  52.  
    try
  53.  
    {
  54.  
    Thread.sleep( 4000);
  55.  
    System.out.println( "四個人一起到達球場,現在開始打球");
  56.  
    System.out.println( "現在對CyclicBarrier進行復用.....");
  57.  
    System.out.println( "又來了一撥人,看看願不願意一起打:");
  58.  
    }
  59.  
    catch(InterruptedException e)
  60.  
    {
  61.  
    e.printStackTrace();
  62.  
    }
  63.  
    //進行復用:
  64.  
    for(int i=0;i<4;i++)
  65.  
    {
  66.  
    threadPool.execute( new GoThread(str1[i]));
  67.  
    }
  68.  
    try
  69.  
    {
  70.  
    Thread.sleep( 4000);
  71.  
    System.out.println( "四個人一起到達球場,表示願意一起打球,現在八個人開始打球");
  72.  
    //System.out.println("現在對CyclicBarrier進行復用");
  73.  
    }
  74.  
    catch(InterruptedException e)
  75.  
    {
  76.  
    e.printStackTrace();
  77.  
    }
  78.  
     
  79.  
     
  80.  
     
  81.  
    }
  82.  
     
  83.  
    }

運行如下程序,得到:

  1.  
    王強開始從宿舍出發
  2.  
    趙傑開始從宿舍出發
  3.  
    李明開始從宿舍出發
  4.  
    劉凱開始從宿舍出發
  5.  
    寢室四兄弟一起出發去球場
  6.  
    王強從樓底下出發
  7.  
    李明從樓底下出發
  8.  
    劉凱從樓底下出發
  9.  
    趙傑從樓底下出發
  10.  
    王強到達操場
  11.  
    李明到達操場
  12.  
    趙傑到達操場
  13.  
    劉凱到達操場
  14.  
    四個人一起到達球場,現在開始打球
  15.  
    現在對CyclicBarrier進行復用.....
  16.  
    又來了一撥人,看看願不願意一起打:
  17.  
    王二開始從宿舍出發
  18.  
    雷兵開始從宿舍出發
  19.  
    洪光開始從宿舍出發
  20.  
    趙三開始從宿舍出發
  21.  
    寢室四兄弟一起出發去球場
  22.  
    洪光從樓底下出發
  23.  
    王二從樓底下出發
  24.  
    雷兵從樓底下出發
  25.  
    趙三從樓底下出發
  26.  
    雷兵到達操場
  27.  
    趙三到達操場
  28.  
    洪光到達操場
  29.  
    王二到達操場
  30.  
    四個人一起到達球場,表示願意一起打球,現在八個人開始打球

        由上面實例可了解CyclicBarrier的工作原理以及復用的特性。

2.通過CountDownLatch實現CyclicBarrier

        CountDownLatch通過將await()方法和countDown()方法在不同線程組分別調用,從而實現線程組間的線程等待,即一個線程組等待另一個線程組執行結束再執行。而CyclicBarrier類則是通過調用await()方法實現線程組內的線程等待,即達到需要攔截的線程數,被攔截的線程才會依次獲取鎖,釋放鎖。那么將CountDownLatch的用法進行轉換,即在同一個線程組內調用await()方法和countDown()方法,則可實現CyclicBarrier類的功能。但是注意的是必須先調用countDown()方法,才能調用await()方法,因為一旦調用await()方法,該線程后面的內容便不再執行,那么count值無法改變。具體代碼如下:

  1.  
    package concurrent;
  2.  
     
  3.  
    import java.util.Vector;
  4.  
    import java.util.concurrent.BrokenBarrierException;
  5.  
    import java.util.concurrent.CountDownLatch;
  6.  
    import java.util.concurrent.LinkedBlockingQueue;
  7.  
    import java.util.concurrent.ThreadPoolExecutor;
  8.  
    import java.util.concurrent.TimeUnit;
  9.  
    public class CyclicBarrierWithCount {
  10.  
    private final static CountDownLatch cdl=new CountDownLatch(3);
  11.  
    private final static ThreadPoolExecutor threadPool= new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());//使用線程池
  12.  
     
  13.  
    private static class GoThread extends Thread{
  14.  
    private final String name;
  15.  
     
  16.  
    public GoThread(String name)
  17.  
    {
  18.  
    this.name=name;
  19.  
     
  20.  
    }
  21.  
    public void run()
  22.  
    {
  23.  
    System.out.println(name+ "開始從宿舍出發");
  24.  
    cdl.countDown();
  25.  
    try
  26.  
    {
  27.  
    Thread.sleep( 1000);
  28.  
    cdl.await(); //攔截線程
  29.  
    System.out.println(name+ "從樓底下出發");
  30.  
    Thread.sleep( 1000);
  31.  
    System.out.println(name+ "到達操場");
  32.  
    }
  33.  
    catch(InterruptedException e)
  34.  
    {
  35.  
    e.printStackTrace();
  36.  
    }
  37.  
     
  38.  
     
  39.  
     
  40.  
    }
  41.  
    }
  42.  
     
  43.  
     
  44.  
    public static void main(String[] args) {
  45.  
    // TODO Auto-generated method stub
  46.  
     
  47.  
     
  48.  
    String[] str= { "李明","王強","劉凱","趙傑"};
  49.  
    String[] str1= { "王二","洪光","雷兵","趙三"};
  50.  
    for(int i=0;i<4;i++)
  51.  
    {
  52.  
    threadPool.execute( new GoThread(str[i]));
  53.  
    }
  54.  
    try
  55.  
    {
  56.  
    Thread.sleep( 4000);
  57.  
    System.out.println( "四個人一起到達球場,現在開始打球");
  58.  
    System.out.println( "現在對CyclicBarrier進行復用.....");
  59.  
    System.out.println( "又來了一撥人,看看願不願意一起打:");
  60.  
    }
  61.  
    catch(InterruptedException e)
  62.  
    {
  63.  
    e.printStackTrace();
  64.  
    }
  65.  
    for(int i=0;i<4;i++)
  66.  
    {
  67.  
    threadPool.execute( new GoThread(str1[i]));
  68.  
    }
  69.  
    try
  70.  
    {
  71.  
    Thread.sleep( 4000);
  72.  
    System.out.println( "四個人一起到達球場,表示願意一起打球,現在八個人開始打球");
  73.  
    //System.out.println("現在對CyclicBarrier進行復用");
  74.  
    }
  75.  
    catch(InterruptedException e)
  76.  
    {
  77.  
    e.printStackTrace();
  78.  
    }
  79.  
    }
  80.  
     
  81.  
    }

執行上述代碼,結果如下:

  1.  
    李明開始從宿舍出發
  2.  
    趙傑開始從宿舍出發
  3.  
    王強開始從宿舍出發
  4.  
    劉凱開始從宿舍出發
  5.  
    王強從樓底下出發
  6.  
    劉凱從樓底下出發
  7.  
    李明從樓底下出發
  8.  
    趙傑從樓底下出發
  9.  
    李明到達操場
  10.  
    趙傑到達操場
  11.  
    王強到達操場
  12.  
    劉凱到達操場
  13.  
    四個人一起到達球場,現在開始打球
  14.  
    現在對CyclicBarrier進行復用.....
  15.  
    又來了一撥人,看看願不願意一起打:
  16.  
    王二開始從宿舍出發
  17.  
    洪光開始從宿舍出發
  18.  
    雷兵開始從宿舍出發
  19.  
    趙三開始從宿舍出發
  20.  
    王二從樓底下出發
  21.  
    洪光從樓底下出發
  22.  
    雷兵從樓底下出發
  23.  
    趙三從樓底下出發
  24.  
    洪光到達操場
  25.  
    王二到達操場
  26.  
    雷兵到達操場
  27.  
    趙三到達操場
  28.  
    四個人一起到達球場,表示願意一起打球,現在八個人開始打球
        由上面可知,CountDownLatch一定情況下可以實現CyclicBarrier類的功能。

 

3.CountDownLatch和CyclicBarrier的比較

1.CountDownLatch是線程組之間的等待,即一個(或多個)線程等待N個線程完成某件事情之后再執行;而CyclicBarrier則是線程組內的等待,即每個線程相互等待,即N個線程都被攔截之后,然后依次執行。

2.CountDownLatch是減計數方式,而CyclicBarrier是加計數方式。

3.CountDownLatch計數為0無法重置,而CyclicBarrier計數達到初始值,則可以重置。

4.CountDownLatch不可以復用,而CyclicBarrier可以復用。


免責聲明!

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



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