CountDownLatch 和 CyclicBarrier

一、CountDownLatch

1、概念:

一个计数器,线程完成一个记录一个,计数器递减,只能用一次。

使一个线程等待其他线程各自执行完毕后再执行。

通过计数器实现,计数器初始值是线程的数量,当每个线程执行完毕后,计数器值 -1,当计数器 = 0时,表示所有线程执行完毕,在闭锁上等待的线程就可以恢复工作。

2、源码:

一个构造器:

// 初始化计数器的值
public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
}

 

三个方法:

// 1、调用 await()方法的线程会被挂起,直到count = 0 才会继续执行
public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

// 2、线程挂起,count = 0 会继续执行,等待时间到达后,如果count != 0 也会执行
public boolean await(long timeout, TimeUnit unit)
    throws InterruptedException {
    return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}

// 3、将 count 值 -1 
public void countDown() {
    sync.releaseShared(1);
}

 

事实用例:

大批量for循环取数据插入数据库。

private static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(30);


public static void main(String[] args) throws Exception {
    ......
    // 初始化
    CountDownLatch countDownLatch=new CountDownLatch(params.size());
    Object[][] data = new Object[params.size()][3];
    for (int i = 0; i < params.size(); i++) {
        Object[] param = params.get(i);
        data[i] = param;
        lockSot(Long.valueOf(param[1].toString()), Integer.valueOf(param[2].toString()), countDownLatch);
    }
    try {
        // 这里进入线程等待,会等待 for循环里所有异步线程执行完毕后,才会往下执行。
        // 如果没有这个等待,就会出现先执行后面代码,for循环的线程后执行完毕的情况。
        countDownLatch.await();
        QueryRunner initRunner = new QueryRunner();
        initRunner.insertBatch(initConn, INSERT_SQL, new ResultSetHandler(){
            @Override
            public Object handle(ResultSet rs) throws SQLException {
                return null;
            }
        }, data);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

public static void lockSot(Long sid, int slot, CountDownLatch countDownLatch) {
    EXECUTOR_SERVICE.execute(() -> {
        //  这里进行异步线程调用
        Map<String, Object> data = Maps.newHashMap();
        data.put("sid", sid);
        data.put("slot", slot);
        Map<String, String> headerMap = Maps.newHashMap();
        headerMap.put("tk", TK);
        if(LOCK_BOOL) {
            OkHttpUtil.post(lock_url, data, headerMap);
        } else {
            OkHttpUtil.post(unlock_url, data, headerMap);
        }
       
        if(countDownLatch != null) {
            // 这里对计数进行 -1
            countDownLatch.countDown();
        }
    });
}

 

 

二、CyclicBarrier

1、概念

计数器是一个阀门,需要所有线程到达,然后继续执行,计数器递增,提供 reset 功能,可以多次复用。

 

两个构造器:

// 初始化线程数
public CyclicBarrier(int parties) {
    this(parties, null);
}

// barrierAction 最后一个到达线程要做的任务。
public CyclicBarrier(int parties, Runnable barrierAction) {
    if (parties <= 0) throw new IllegalArgumentException();
    this.parties = parties;
    this.count = parties;
    this.barrierCommand = barrierAction;
}

 

重要方法:

// 线程调用 await() 表示已经达到阈值
// BrokenBarrierException 表示阈值被破坏,原因可能是其中一个线程 await() 时被中断或超时
public int await() throws InterruptedException, BrokenBarrierException {
    try {
        return dowait(false, 0L);
    } catch (TimeoutException toe) {
        throw new Error(toe); // cannot happen
    }
}

// 2、线程挂起,count = 阈值 会继续执行,等待时间到达后,如果count != 阈值 也会执行
public int await(long timeout, TimeUnit unit)
    throws InterruptedException,
           BrokenBarrierException,
           TimeoutException {
    return dowait(true, unit.toNanos(timeout));
}

// 3、重置栅栏 
public void reset() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        breakBarrier();   // break the current generation
        nextGeneration(); // start a new generation
    } finally {
        lock.unlock();
    }
}

 

事实用例:

暂无

 

posted @ 2021-11-05 09:56  currentTimeMillis  阅读(62)  评论(0编辑  收藏  举报