1、CountDownLatch和CyclicBarrier作用

  CountDownLatch和CyclicBarrier都位于java.util.concurrent包中,都具有计数功能,一般用于多线程间的协作。

  CountDownLatch是减法计数器,子线程中调用countDown()计数减1,主线程调用await()阻塞线程执行,直到计数为0才会让线程继续执行,CountDownLatch不可重复利用。

  CyclicBarrier是加法计数器,子线程调用await()计数加1,同时阻塞线程执行,直到兄弟线程都执行了await(),计数到达了设定值,CyclicBarrier可重复使用,计数到达指定值后重新开始。CyclicBarrier构造函数设定一个Runnable参数,通知计数已到达指定值。

2、使用示例

package com.zhi.test;

import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;

import org.junit.Test;

/**
 * CyclicBarrier和CountDownLatch使用样例
 * 
 * @author 张远志
 * @since 2020年5月4日18:01:00
 *
 */
public class MyTest {
    @Test
    public void test() {
        int jobCount = 5;
        CountDownLatch latch = new CountDownLatch(jobCount);
        CyclicBarrier barrier = new CyclicBarrier(jobCount, new Runnable() {
            public void run() {
                System.out.println("所有线程都到了");
            }
        });
        for (int i = 1; i <= jobCount; i++) {
            new Thread(new Job(latch, barrier, i)).start();
        }
        try {
            latch.await();
        } catch (Exception e) {
        }
        System.out.println("所有线程已执行完成!");
    }

    class Job implements Runnable {
        private CountDownLatch latch;
        private CyclicBarrier barrier;
        private final int id;

        public Job(CountDownLatch latch, CyclicBarrier barrier, int id) {
            this.latch = latch;
            this.barrier = barrier;
            this.id = id;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(new Random().nextInt(10) * 1000);
                System.out.println("线程" + id + "到达栅栏1");
                barrier.await();
            } catch (Exception e) {
            }
            try {
                System.out.println("线程" + id + "到达栅栏2");
                barrier.await();
            } catch (Exception e) {
            }
            System.out.println("任务" + id + "执行完成");
            latch.countDown();
        }
    }
}

  执行结果:

线程2到达栅栏1
线程4到达栅栏1
线程5到达栅栏1
线程3到达栅栏1
线程1到达栅栏1
所有线程都到了
线程1到达栅栏2
线程2到达栅栏2
线程5到达栅栏2
线程4到达栅栏2
线程3到达栅栏2
所有线程都到了
任务3执行完成
任务1执行完成
任务2执行完成
任务4执行完成
任务5执行完成
所有线程已执行完成!

3、如何保证计数的线程安全性

  CountDownLatch的countDown会进行加法计数,CyclicBarrier的await()会进行加法计数,查看源码我们会发现,这2个方法都没有使用synchronized关键字,那他们有时如何保证线程安全性的呢?

  查看CountDownLatch源码,我们看到CountDownLatch使用了Sync内部类(继承AbstractQueuedSynchronizer)做减法,通过Unsafe类比较并设置新值。

 

  查看CyclicBarrier源码,我们看到ReentrantLock锁。为啥它与CountDownLatch不一样呢,因为CyclicBarrier不仅要完成计数,还需要调用barrierCommand,并且要实现重复可用(将count值还原),其实ReentrantLock内部也使用Unsafe类进行了原子操作。

 

posted on 2019-01-16 09:38  玄同太子  阅读(287)  评论(0编辑  收藏  举报