多线程003 - 再谈CyclicBarrier

  java.util.concurrent.CyclicBarrier也是JDK 1.5提供的一个同步辅助类(为什么用也呢?參见再谈CountDownLatch)。它同意一组线程互相等待,直到到达某个临界点(a common barrier point,翻译成公共障碍点、公共栅栏点都不够传神,直接用临界点吧)。在某个程序中,一组固定大小的线程必须互相等待时。CyclicBarrier将起非常大的作用。由于在等待线程被释放后,这个临界点能够重用。所以说是循环的。

  CyclicBarrier支持一个可选的Runnable。在一组线程中的最后一个线程完毕之后、释放全部线程之前,该Runnable在屏障点执行一次(每循环一次Runnable执行一次)。这样的方式能够用来在下一波继续执行的线程执行之前更新共享状态(比方下一波僵尸来之前。检查武器弹药)。

CountDownLatch与CyclicBarrier

  CountDownLatch是不可以反复使用的。是一次性的,其锁定一经打开。就不可以在反复使用。

就像引线。点燃后就在燃烧降低。燃烧完了就不能再次使用了。

CyclicBarrier是一种循环的方式进行锁定,这次锁定被打开之后,还可以反复计数。再次使用。就像沙漏。这次漏完了。倒过来接着漏。

  另一点是两者之间非常大的差别,就是CountDownLatch在等待子线程的过程中,会锁定主线程,而CyclicBarrier不会锁定主线程,仅仅是在全部子线程结束后。依据定义运行其可选的Runnable线程。

  所以在这两种辅助类中进行选择时,可以非常明显进行区分。

CyclicBarrier实例

  能够考虑这么一种情况,我们须要向数据库导入一些数据,没导入几条希望能进行一次计时,便于我们查看。由于实现比較简单,直接上代码:

package howe.demo.thread.cyclicbarrier;

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

/**
 * @author liuxinghao
 * @version 1.0 Created on 2014年9月17日
 */
public class CyclicBarrierTest {
    public static void main(String[] args) throws InterruptedException {
        final long start = System.currentTimeMillis();
        final CyclicBarrier barrier = new CyclicBarrier(3, new Runnable() {
            @Override
            public void run() {
                long end = System.currentTimeMillis();
                System.out.println("导入" + 3 + "条数据,至此总共用时:" + (end - start)
                        + "毫秒");
            }
        });

        for (int i = 0; i < 9; i++) {
            final int threadID = i + 1;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(new Random().nextInt(10));// 模拟业务操作
                        System.out.println(threadID + "完毕导入操作。

"); barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } }).start(); } System.out.println("====主线程结束===="); } }

运行结果为:

====主线程结束====
4完毕导入操作。

2完毕导入操作。 1完毕导入操作。 导入3条数据,至此总共用时:4006毫秒 5完毕导入操作。 6完毕导入操作。 8完毕导入操作。 导入3条数据。至此总共用时:4007毫秒 3完毕导入操作。

0完毕导入操作。 7完毕导入操作。 导入3条数据。至此总共用时:8006毫秒

  程序没导入3条会进行一次计时,统计已经运行的时间。

假设CyclicBarrier构造函数的数字和for循环的次数相等的话,这个就是总共用时。

扩展

  考虑一下上面的样例,假设for循环的次数不是CyclicBarrier监听次数的整数倍,比方是10。那运行结果将会是:

====主线程结束====
2完毕导入操作。
5完毕导入操作。

4完毕导入操作。

导入3条数据,至此总共用时:4005毫秒 8完毕导入操作。 1完毕导入操作。

3完毕导入操作。 导入3条数据。至此总共用时:5005毫秒 7完毕导入操作。

6完毕导入操作。

0完毕导入操作。

导入3条数据。至此总共用时:8005毫秒 9完毕导入操作。

  在打印完“9完毕导入操作。

”之后,将一直等待。

在这里能够通过barrier.getNumberWaiting()查看还差多少个线程达到屏障点。

假设出现这样的情况。那就须要和CountDownLatch配合使用了。当子线程所有运行完。有推断barrier.getNumberWaiting()不等于0,则调用barrier.reset()重置。这个时候将会触发BrokenBarrierException异常,可是将结束整个过程。

改动的代码例如以下:

package howe.demo.thread.cyclicbarrier;

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

/**
 * @author liuxinghao
 * @version 1.0 Created on 2014年9月17日
 */
public class CyclicBarrierTest {
    public static void main(String[] args) throws InterruptedException {
        final long start = System.currentTimeMillis();
        final CountDownLatch count = new CountDownLatch(10);
        final CyclicBarrier barrier = new CyclicBarrier(3, new Runnable() {
            @Override
            public void run() {
                long end = System.currentTimeMillis();
                System.out.println("导入" + 3 + "条数据,至此总共用时:" + (end - start)
                        + "毫秒");
            }
        });

        for (int i = 0; i < 10; i++) {
            final int threadID = i + 1;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(new Random().nextInt(10));// 模拟业务操作
                        System.out.println(threadID + "完毕导入操作。

"); count.countDown(); barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { System.out.println("触发BrokenBarrierException异常。"); } } }).start(); } count.await(); if(barrier.getNumberWaiting() != 0) { System.out.println("不是整数倍。

都已运行完,重置CyclicBarrier。

"); barrier.reset(); } System.out.println("====主线程结束===="); } }

运行结果为:

3完毕导入操作。
9完毕导入操作。

6完毕导入操作。 导入3条数据,至此总共用时:3005毫秒 8完毕导入操作。 5完毕导入操作。

10完毕导入操作。 导入3条数据。至此总共用时:7005毫秒 1完毕导入操作。 7完毕导入操作。 4完毕导入操作。 2完毕导入操作。 导入3条数据,至此总共用时:9005毫秒 不是整数倍。都已运行完,重置CyclicBarrier。 ====主线程结束==== 触发BrokenBarrierException异常。

  使用barrier.reset()进行重置,由于CyclicBarrier是一个循环,开头就是结尾,所以重置也能够理解为直接完毕。

  另外。由于使用了CountDownLatch。所以主线程会锁定,直到线程通过count.await()向下运行。

posted @ 2017-06-04 15:09  clnchanpin  阅读(266)  评论(0编辑  收藏  举报