【问题标题】:ForkJoinPool, Phaser and managed blocking: to what extent do they works against deadlocks?ForkJoinPool、Phaser 和托管阻塞:它们在多大程度上对抗死锁?
【发布时间】:2015-08-04 05:50:48
【问题描述】:

这个小代码 sn-p 在 jdk8u45 上永远不会完成,而过去在 jdk8u20 上可以正常完成:

public class TestForkJoinPool {

    final static ExecutorService pool = Executors.newWorkStealingPool(8);
    private static volatile long consumedCPU = System.nanoTime();

    public static void main(String[] args) throws InterruptedException {
        final int numParties = 100;
        final Phaser p = new Phaser(1);
        final Runnable r = () -> {
            p.register();
            p.arriveAndAwaitAdvance();
            p.arriveAndDeregister();
        };

        for (int i = 0; i < numParties; ++i) {
            consumeCPU(1000000);
            pool.submit(r);
        }

        while (p.getArrivedParties() != numParties) {}
    }

    static void consumeCPU(long tokens) {
        // Taken from JMH blackhole
        long t = consumedCPU;
        for (long i = tokens; i > 0; i--) {
            t += (t * 0x5DEECE66DL + 0xBL + i) & (0xFFFFFFFFFFFFL);
        }
        if (t == 42) {
            consumedCPU += t;
        }
    }
}

doc of phaser 声明

Phasers 也可以被 ForkJoinPool 中执行的任务使用,这将确保在其他人被阻塞等待阶段推进时执行任务有足够的并行性。

但是javadoc of ForkjoinPool#mangedBlock 声明:

如果在 ForkJoinPool 中运行,可以先扩展池以确保足够的并行度

那里只有一个可能。所以我不确定这是否是一个错误,或者只是不依赖 Phaser/ForkJoinPool 合约的错误代码:Phaser/ForkJoinPool 组合的合约有多难防止死锁?


我的配置:

  1. Linux adc 3.14.27-100.fc19.x86_64 #1 SMP Wed Dec 17 19:36:34 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
  2. 8 核 i7

【问题讨论】:

  • @AlekseyShipilev 也许对这个问题有更多的见解:如果我删除 comsumeCpu() 部分,或者减少令牌的数量,测试会正确完成。如果我在 ForkJoinPool#tryCompensate 中设置断点并手动执行,则同样的情况

标签: java java-8 phaser forkjoinpool


【解决方案1】:

您的问题似乎来自 JDK 8u20 和 8u45 之间的 ForkJoinPool 代码更改。

在 u20 中,ForkJoin 线程在被回收之前总是至少存活 200 毫秒(请参阅 ForkJoinPool.FAST_IDLE_TIMEOUT)。

在 u45 中,一旦 ForkJoinPool 达到其目标并行度加上 2 个额外线程,线程将在它们用完工作时立即死亡而无需等待。 您可以在 ForkJoinPool.java(第 1810 行)的 awaitWork 方法中看到这种变化:

    int t = (short)(c >>> TC_SHIFT);  // shrink excess spares
    if (t > 2 && U.compareAndSwapLong(this, CTL, c, prevctl))
        return false; 

您的程序使用 Phasers 任务来创建额外的工作人员。每个任务都会产生一个新的补偿工作者,用于接收下一个提交的任务。
但是,一旦达到目标并行度 + 2,补偿工作者将立即死亡,无需等待,也没有机会接取之后立即提交的任务。

我希望这会有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多