深入学习java并发编程:CountDownLatch、CyclicBarrier

原创 2016年06月01日 22:05:40
      在java并发包中提供了一些非常有用的辅助类来帮助我们进行并发编程,这些类包括CountDownLatch、CyclicBarrier以及Semaphore。
1、CountDownLatch

1 ) 类图
  

2)CountDownLatch实现
      CountDownLatch同步组件同ReetrantLock一样,也是基于抽象队列同步器,在内部聚合引用了自定义AQS类Syn的一个实例对象。在类Syn中,重写了tryAcquireShared方法以及tryReleaseShared方法,实现以共享的方式获取同步状态。CountDownLatch可以实现类似计数器的功能,比如有一个任务,它要等待其他多个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能。

     Syn自定义AQS源码如下:

private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;
        Sync(int count) {    //初始设置同步计数状态
            setState(count);
        }
        int getCount() {
            return getState();
        }
        protected int tryAcquireShared(int acquires) {   //只有同步状态减为0时,才能够成功获取到同步状态,在此之前,所有获取线程都将阻塞
            return (getState() == 0) ? 1 : -1;
        }
        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))   //每次释放同步状态,将同步计数减1,当减为0,是返回true,将唤醒阻塞线程
                    return nextc == 0;
            }
        }
    }
   CountDownLatch代码实现:
      CountDownLatch构造器中,传入同步计数值,并以此创建了 Sync对象。
     public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }
  CountDownLatch的await()方法,通过调用sync的acquireSharedInterruptibly()方法,共享获取同步状态。当计算值减为0时,获取成功,否则,将阻塞,等待唤醒。
   public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
 CountDownLatch的countDown()方法,通过sync的releaseShared方法释放同步状态,减少计数值,进行倒计数,当减为0,唤醒阻塞线程。
 public void countDown() {
        sync.releaseShared(1);
    }
3)CountDownLatch应用实例
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
public class Test {
     public static void main(String[] args) {   
         final CountDownLatch latch = new CountDownLatch(2);
          
         new Thread(){
             public void run() {
                 try {
                     System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
                    Thread.sleep(3000);
                    System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
                    latch.countDown();
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
             };
         }.start();
          
         new Thread(){
             public void run() {
                 try {
                     System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
                     Thread.sleep(3000);
                     System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
                     latch.countDown();
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
             };
         }.start();
          
         try {
             System.out.println("等待2个子线程执行完毕...");
            latch.await();
            System.out.println("2个子线程已经执行完毕");
            System.out.println("继续执行主线程");
        catch (InterruptedException e) {
            e.printStackTrace();
        }
     }
}

2、CyclicBarrier
1)类图

2)CyclicBarrier实现
         CyclicBarrier同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),在该屏障点运行一次。
        CyclicBarrier通过内部引用ReentrantLock可重入锁对象,实现多线程之间的同步控制。
   /** The lock for guarding barrier entry */
    private final ReentrantLock lock = new ReentrantLock();  //可重入锁
    /** Condition to wait on until tripped */
    private final Condition trip = lock.newCondition();     //Condition对象,用于控制线程间等待/通知


CyclicBarrier构造器:
      public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;   //置计数值
        this.barrierCommand = barrierAction;
    }

CyclicBarrier  dowait(boolean timed, long nanos)函数 :
   线程执行await()函数表示到达公共屏障点 (common barrier point),进而调用dowait函数,在该函数中,首先获取锁,然后将count计数值减1,如果计数值不为0,那么就调用Condition 对象await方法等待,如果计数值变为0,就表示所用任务都到达屏障点,如barrierAction不为空,则运行,最后通过调用Condition 对象signalAll()方法,唤醒所用线程。
 public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen;
        }
    }
 private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            final Generation g = generation;
            if (g.broken)
                throw new BrokenBarrierException();
            if (Thread.interrupted()) {
                breakBarrier();
                throw new InterruptedException();
            }
           int index = --count;
           if (index == 0) {  // tripped
               boolean ranAction = false;
               try {
                   final Runnable command = barrierCommand;
                   if (command != null)
                       command.run();
                   ranAction = true;
                   nextGeneration();
                   return 0;
               } finally {
                   if (!ranAction)
                       breakBarrier();
               }
           }
            // loop until tripped, broken, interrupted, or timed out
            for (;;) {
                try {
                    if (!timed)
                        trip.await();
                    else if (nanos > 0L)
                        nanos = trip.awaitNanos(nanos);
                } catch (InterruptedException ie) {
                    if (g == generation && ! g.broken) {
                        breakBarrier();
                        throw ie;
                    } else {
                        // We're about to finish waiting even if we had not
                        // been interrupted, so this interrupt is deemed to
                        // "belong" to subsequent execution.
                        Thread.currentThread().interrupt();
                    }
                }
                if (g.broken)
                    throw new BrokenBarrierException();
                if (g != generation)
                    return index;
                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();
        }
    }
3 ) CyclicBarrier应用示例

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
public class Test {
    public static void main(String[] args) {
        int N = 4;
        CyclicBarrier barrier  = new CyclicBarrier(N,new Runnable() {
            @Override
            public void run() {
                System.out.println("当前线程"+Thread.currentThread().getName());   
            }
        });
         
        for(int i=0;i<N;i++)
            new Writer(barrier).start();
    }
    static class Writer extends Thread{
        private CyclicBarrier cyclicBarrier;
        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }
 
        @Override
        public void run() {
            System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
            try {
                Thread.sleep(5000);      //以睡眠来模拟写入数据操作
                System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
                cyclicBarrier.await();
            catch (InterruptedException e) {
                e.printStackTrace();
            }catch(BrokenBarrierException e){
                e.printStackTrace();
            }
            System.out.println("所有线程写入完毕,继续处理其他任务...");
        }
    }
}

3、总结
     CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:

  1)CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;

  2)CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;

  3)CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。








java并发编程中CountDownLatch和CyclicBarrier的使用

java并发编程中CountDownLatch和CyclicBarrier的使用  在多线程程序设计中,经常会遇到一个线程等待一个或多个线程的场景,遇到这样的场景应该如何解决?如果是一个线程等待一个线...
  • hbzyaxiu520
  • hbzyaxiu520
  • 2011年02月14日 12:47
  • 6417

线程执行顺序——CountDownLatch、CyclicBarrier 、join()、线程池

本文主要围绕一个问题展开:线程执行顺序,比如某个线程在其他线程并发执行完毕后最后执行。join介绍join()是Thread类的一个方法,join()方法的作用是等待这个线程结束。t.join()方法...
  • lzx_2011
  • lzx_2011
  • 2017年03月10日 00:02
  • 920

CyclicBarrier和CountDownLatch区别

这两天写多线程时,用到了CyclicBarrier,下意识的认为CyclicBarrier和CountDownLatch作用很像,就翻阅资料查了一下,说一下他们的区别吧 CyclicBarr...
  • tolcf
  • tolcf
  • 2016年03月18日 19:19
  • 6509

CountDownLatch和CyclicBarrier的应用场景

jdk1.5之后,java的concurrent包提供了一些并发工具类,比如CountDownLatch和CyclicBarrier,这里只讲它们的应用场景,暂不作原理剖析。 CountDownLa...
  • suibo0912hf
  • suibo0912hf
  • 2015年09月15日 09:07
  • 1035

[沧海拾遗]java并发之CountDownLatch、Semaphore和CyclicBarrier

JAVA并发包中有三个类用于同步一批线程的行为,分别是CountDownLatch、Semaphore和CyclicBarrier。 CountDownLatch CountDownLat...
  • yanhandle
  • yanhandle
  • 2013年06月03日 19:30
  • 6476

CountDownLatch、CyclicBarrier、Semaphore共同之处与区别以及各自使用场景

摘要: jdk1.5之后,java的concurrent包提供了一些并发工具类,比如CountDownLatch和CyclicBarrier,Semaphore。这里简要的比较一下他们的共同之处与区别...
  • jackyechina
  • jackyechina
  • 2016年10月26日 10:55
  • 2240

CountDownLatch, CyclicBarrier, Phaser 总结

CountDownLatch的一个非常典型的应用场景是:有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行。假如我们这个想要继续往下执行的任务调用一个CountDownLatc...
  • walkerJong
  • walkerJong
  • 2016年02月03日 15:55
  • 753

闭锁CountDownLatch和栅栏CyclicBarrier之异同举例

CountDownLatch和CyclicBarrier的主要联系和区别如下: 1.闭锁CountDownLatch做减计数,而栅栏CyclicBarrier则是加计数。 2.CountDownLat...
  • gaolu
  • gaolu
  • 2015年05月29日 09:02
  • 10783

CountDownLatch与CyclicBarrier详解

1. CountDownLatch 1.1 简介 CountDownLatch是一个同步辅助类,通过它可以完成类似于阻塞当前线程的功能,即:一个线程或多个线程一直等待,直到其他线程执行的操作完成。...
  • xianymo
  • xianymo
  • 2015年07月13日 17:34
  • 1125

CountDownLatch和CyclicBarrier原理的分析理解

学concurrent并发包的时候,看到countDownLatch和CyclicBarrier有些相似的地方.所以放在一起进行学习分析一下. 首先看一下jdk注释的第一句话简单阐明二者各自的意...
  • ruanjian1111ban
  • ruanjian1111ban
  • 2017年02月27日 11:48
  • 219
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深入学习java并发编程:CountDownLatch、CyclicBarrier
举报原因:
原因补充:

(最多只允许输入30个字)