继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

phaser替换countdownlatch

xpbob
关注TA
已关注
手记 152
粉丝 1.6万
获赞 380

countdownlatch

countdownlatch是我们常用的同步类。但是在使用过程中有一个问题,就是他需要确定同步的次数。当我们的次数有变化的时候,就会对修改带来一定的问题。新增或者减少一个同步任务,只要次数没有跟着改变就会出现运行的问题。

  1. 新增了任务,会发现同步时数据不准确。同步线程比预期快运行。
  2. 减少任务,会发现线程一直等待,无法运行下去。

针对这种场景,我们常见的一种解决方法是维护一个数组,数组里是任务,countdownlatch的数量限制从数组获取,创建的任务也从数组获取。这样通过约定相同的数据源。满足变更的需要。这样就有一个地方是需要注意的。就是每个task里的countdownlatch是需要额外设置的。刚开始的时候countdownlatch也无法确认有多少的任务。所以经常看到的写法的样例如下。
首选构造一个可以填充countDownLatch的Runnable子类。

    class Task implements Runnable {
        CountDownLatch countDownLatch;

        public void setCountDownLatch(CountDownLatch countDownLatch) {
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            try {
                System.out.println("over");
            } finally {
                countDownLatch.countDown();
            }
        }
    }

然后就是创建操作的流程了

        Task[] runnables = new Task[]{new Task()};
        CountDownLatch countDownLatch =new CountDownLatch(runnables.length);
        for (Task runnable : runnables) {
            runnable.setCountDownLatch(countDownLatch);
            executorService.submit(runnable);
        }
        countDownLatch.await();

这样就做到了只添加任务,不需要管理其他的部分了。但是这也有一个问题。对java开发不友好。当task处理的任务没有相似性的时候,似乎lambda会带来更多的方便。lambda的操作还是比较单一,无法与上面的情况互相配合,lambda的时候就必须确定countDownLatch的个数了,所以无法把lambda表达式写到task数组里去。那是不是有方法可以动态的确定任务个数呢。phaser可以做到。

phaser

phaser的写法和countDownLatch类似,不过他自己又了维护增加和减少的方法。在每个task里,需要自己做增加和减少的操作。

        Phaser phaser = new Phaser();
        phaser.register();
        executorService.submit(() -> {
            try {
                Thread.sleep(3000);
                System.out.println("hello");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                phaser.arrive();
            }
        });
        phaser.register();
        phaser.arriveAndAwaitAdvance();

上面的demo中。
phaser.register();调用了2次。这两次的含义是不一样的。
第一次的是为了submit提交,他对应的是线程里的phaser.arrive();
第二次的是为了主线程阻塞同步, 利用phaser.arriveAndAwaitAdvance();进行同步操作,所以先自己做个增加。
看到这里或许有个疑问,就是为什么第一次的register不放在线程池内执行。
phaser本身判定的是次数是否为0。如果线程池进行了排队。那么线程的内容就无法被执行。register久久不能触发。
这样就会导致主线程自己做了增减,完成了判定为0的情况。
countDownLatch的一些问题phaser也有。就是线程池的拒绝策略触发时,线程内容无法被执行,所以无法触发减数操作,他们都会一直等待下去。

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP