Java线程池之栅栏

版权声明:未经本人允许,严禁转载或者抄袭 https://blog.csdn.net/lmh_19941113/article/details/85049025

 通过闭锁来启动一组相关的操作,或者等待一组相关的操作结束。闭锁是一次性对象,一旦进入终止状态,就不能重置。

 栅栏类似于闭锁,它能阻塞到一组线程到某个事件发生。栅栏于闭锁的关键区别在于,所有线程必须同时到达栅栏位置,才能继续执行。闭锁用于等待事件,而栅栏用于等待其他线程。栅栏用于实现一些协议,例如几个家庭决定在某个地方集合:”所有人6:00在麦当劳碰头,到了以后要等待其他人,之后再讨论下一步要做的事情。“

CyclicBarrier可以使一定数量的参与方法反复地在栅栏位置汇集,它在并行迭代算法中非常有用:这种算法通常将一个问题拆分成一系列独立的子问题。当线程到达栅栏位置时将调用await方法,这个方法将阻塞直到所有线程都到达栅栏位置。如果所有线程都到达了栅栏位置,那么栅栏将打开,此时所有线程将被释放,而栅栏将被重置以便下次使用。如果对await的调用超时,或者await阻塞的线程被中断,那么栅栏将被认为是打破了,所有阻塞的await调用都将终止并抛出BrokenBarrierException。如果成功通过栅栏那么将为每个线程返回一个唯一的到达索引号,我们可以利用这些索引号来“选举”产生一个领导线程,并在下一波迭代中由该领导线程执行一些特殊化的工作。CyclicBarrier还可以使你将一个栅栏操作传递给构造函数,这是一个Runnable,当成功通过栅栏时会(在一个子线程中)执行它,但在阻塞线程被释放之前是不能执行的。

public static void main(String args[]) throws InterruptedException {
        ExecutorService executors = Executors.newFixedThreadPool(10);
        CyclicBarrier cyclicBarrier = new CyclicBarrier(10, new Runnable() {
            @Override
            public void run() {//释放栅栏时调用,也就是最后一个执行完毕的线程调用
                System.out.println(Thread.currentThread().getName() + "释放所有线程");
            }
        });
        for (int i = 0; i < 10; i++) {
            executors.submit(new CycRunnable(cyclicBarrier));
        }
        System.out.println("执行完毕");
    }

    private static class CycRunnable implements Runnable {

        private CyclicBarrier cyclicBarrier;

        public CycRunnable(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + "开始执行:");
            try {
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName() + "执行完毕并且等待");
                cyclicBarrier.await();
                System.out.println(Thread.currentThread().getName() + "等待结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

**注意:**如果线程数大于传入给CyclicBarrier 的值,则多余线程会一直等待,如果小于则所有线程会一直等待,CyclicBarrier 也无法释放。

 栅栏还有另外一种形式,那就是Exchanger,它是一种两方栅栏,各方在栅栏位置交换数据。当两方执行不对称的操作时,Exchanger会非常有用,例如当一个线程向缓冲区写入数据,而另外一个线程从缓冲区中读取数据。这些线程可以使用Exchanger来汇合,并将满的缓冲区与空的缓冲区交换。当两个线程通过Exchanger交换对象时,这种交换就把这两个对象安全的交给发布给另一方。
 数据交换的时机取决于应用程序的响应需求。最简单的方案是,当缓冲区被填满时,由填充任务进行交换,当缓冲区为空时,由清空任务进行交换。这样会把需要交换的次数降至最低,但如果新数据的到达率不可预测,那么一些数据的处理过程就会延迟。另外一个方法是,不仅当缓冲区被填满时进行交换,并且当缓冲区被填充到一定程度并保持一定时间后,也进行交换。

猜你喜欢

转载自blog.csdn.net/lmh_19941113/article/details/85049025