zoukankan      html  css  js  c++  java
  • 栅栏

      栅栏作用类似闭锁,不同点在于闭锁是一次性用品,栅栏可重复使用。另外闭锁的await方法是用来阻塞的,栅栏的await方法则类似闭锁的countDown方法。下面举例说明,使用线程池来调度多线程:

    package com.wulinfeng.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
    ", 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类参见http://www.cnblogs.com/wuxun1997/p/6822868.html

    package com.wulinfeng.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
    ", 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;
        }
    }
  • 相关阅读:
    Java的内存结构(Memory Structure)和垃圾收集(Garbage Collection)图解
    走遍天下的三大vb控制结构
    程序员快速阅读,绝对不是神话
    Android4.0.3源码分析——开机流程之Zygote
    云端的天使
    提高班的“伞”
    Android 4.0.3 源代码结构分析(一)
    如何在Java中选择Map/List/Set
    关于Hadoop中reducer端combiner的一些思考
    vb添加GIF动态图片
  • 原文地址:https://www.cnblogs.com/wuxun1997/p/6816553.html
Copyright © 2011-2022 走看看