栅栏CyclicBarrier

  栅栏作用类似闭锁,不同点在于闭锁是一次性用品,栅栏可重复使用。另外闭锁的await方法是用来阻塞的,栅栏的await方法则类似闭锁的countDown方法,是用来做减法的,当栅栏初始化的个数减为零后,栅栏便执行它在初始化时指定的Runnable.run方法。

  下面举例说明,使用线程池来调度多线程:

package com.wlf.concurrent;

import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class BarrierHarness {

    public void timeTasks(int num) {

        final long start = System.nanoTime();
        System.out.println("开始计时...");

        CyclicBarrier barrier = new CyclicBarrier(num, new Runnable() {
            // 栅栏动作,在计数器为0的时候执行
            @Override
            public void run() {
                long end = System.nanoTime();
                System.out.printf("战斗结束,耗时: %d 毫秒", (end - start) / 1000000);
            }
        });

        // 使用线程池调度并发线程
        ExecutorService es = Executors.newCachedThreadPool();

        try {
            for (int i = 0; i < num; i++) {
                es.execute(new Soldier(i, barrier));
            }
        } finally {
            es.shutdown();
        }
    }

    /**
     * 具体执行命令的线程
     * 
     * @author Administrator
     *
     */
    class Soldier implements Runnable {
        int num;
        CyclicBarrier barrier;

        public Soldier(int num, CyclicBarrier barrier) {
            this.num = num;
            this.barrier = barrier;
        }

        @Override
        public void run() {
            System.out.printf("我是士兵 %2d 号,现在进入战斗线程中,获取战斗任务编号:%d\n", num, new Random().nextInt());

            // 栅栏计数器减1
            try {
                barrier.await();
            } catch (InterruptedException e) {
                return;
            } catch (BrokenBarrierException e) {
                return;
            }
        }
    }

    public static void main(String[] args) {
        new BarrierHarness().timeTasks(7);
    }
}

  运行结果:

开始计时...
我是士兵  0 号,现在进入战斗线程中,获取战斗任务编号:-961128239
我是士兵  6 号,现在进入战斗线程中,获取战斗任务编号:529020377
我是士兵  4 号,现在进入战斗线程中,获取战斗任务编号:-1723358857
我是士兵  2 号,现在进入战斗线程中,获取战斗任务编号:-671078051
我是士兵  5 号,现在进入战斗线程中,获取战斗任务编号:-1096701822
我是士兵  3 号,现在进入战斗线程中,获取战斗任务编号:1475521415
我是士兵  1 号,现在进入战斗线程中,获取战斗任务编号:1634562126
战斗结束,耗时: 62 毫秒

   再看一个例子,这里反复使用同一个栅栏来实现同一时刻的并发执行和数据返回,其中用到的BoundedBuffer类参见信号量Semaphore使用实例

package com.wlf.concurrent;

import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

public class PutTakeTest {
    private static final ExecutorService pool = Executors.newCachedThreadPool();
    // 计算所有生产者线程的随机数总和
    private final AtomicInteger putSum = new AtomicInteger(0);
    // 计算所有消费者线程的随机数总和
    private final AtomicInteger takeSum = new AtomicInteger(0);
    private final CyclicBarrier barrier;
    private final BoundedBuffer<Integer> bb;
    private final int nTrials, nPairs;

    public static void main(String[] args) {
        new PutTakeTest(10, 10, 100000).test();
        pool.shutdown();
    }

    PutTakeTest(int capacity, int npairs, int ntrials) {
        this.bb = new BoundedBuffer<Integer>(capacity);
        this.nTrials = ntrials; // 每个线程累加次数
        this.nPairs = npairs; // 生产/消费的线程数
        // 栅栏需要拦截的线程数量是:生产者+消费者+主线程
        this.barrier = new CyclicBarrier(npairs * 2 + 1);
    }

    void test() {
        try {
            for (int i = 0; i < nPairs; i++) {
                pool.execute(new Producer()); // 提交生产者任务
                pool.execute(new Consumer()); // 提交消费者任务
            }
            barrier.await(); // 等待所有线程就绪,生产和消费任务都已提交后再同时开始并发执行生产和消费
            barrier.await(); // 等待所有线程执行完成后,再同时获取生产和消费的统计结果
            System.out.printf("putSum: %d, takeSum: %d\n", putSum.get(), takeSum.get());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 生产者
     * 
     * @author Administrator
     *
     */
    class Producer implements Runnable {
        public void run() {
            try {
                // 获取随机数种子
                int seed = (this.hashCode() ^ (int) System.nanoTime());
                int sum = 0;
                barrier.await(); // 再一次使用栅栏让生产者到齐后才执行下面逻辑
                for (int i = nTrials; i > 0; --i) {
                    bb.put(seed); // 放入有界队列
                    sum += seed; // 求和
                    seed = xorShift(seed); // 获取下一个随机数
                }
                putSum.getAndAdd(sum);
                barrier.await(); // 再一次使用栅栏让生产者执行完毕后再返回结果
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    class Consumer implements Runnable {
        public void run() {
            try {
                barrier.await();
                int sum = 0;
                for (int i = nTrials; i > 0; --i) {
                    sum += bb.take();
                }
                takeSum.getAndAdd(sum);
                barrier.await();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    private static int xorShift(int y) {
        y ^= (y << 6);
        y ^= (y >>> 21);
        y ^= (y << 7);
        return y;
    }
}

   运行结果是随机的:

putSum: -471766499, takeSum: -471766499

 

posted on 2017-05-06 13:57  不想下火车的人  阅读(242)  评论(0编辑  收藏  举报

导航