关闭

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

标签: CountDownLatchCyclicBarrier并发java
138人阅读 评论(0) 收藏 举报
分类:
      在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是可以重用的。








0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:26520次
    • 积分:710
    • 等级:
    • 排名:千里之外
    • 原创:45篇
    • 转载:11篇
    • 译文:0篇
    • 评论:1条