7

是否可以配置ForkJoinPool为使用 1 个执行线程?

我正在执行RandomForkJoinPool. 每次运行时,我都会得到不同的运行时行为,这使得研究回归变得困难。

我希望代码库提供“调试”和“发布”模式。“调试”模式将配置Random固定种子和ForkJoinPool单个执行线程。“发布”模式将使用系统提供的Random种子并使用默认ForkJoinPool线程数。

我尝试ForkJoinPool使用 1 的并行度进行配置,但它使用 2 个线程(main和第二个工作线程)。有任何想法吗?

4

2 回答 2

8

所以,事实证明我错了。

当您将 a 配置ForkJoinPoolparallelism1 时,只有一个线程执行任务。main线程在 上被阻塞ForkJoin.get()。它实际上不执行任何任务。

也就是说,事实证明,提供确定性行为确实很棘手。以下是我必须纠正的一些问题:

  • ForkJoinPool如果工作线程空闲时间足够长,则正在使用不同的工作线程(具有不同的名称)执行任务。例如,如果主线程在调试断点处挂起,则工作线程将变得空闲并关闭。当我恢复执行时,ForkJoinThread会启动一个具有不同名称的新工作线程。为了解决这个问题,我必须提供一个自定义ForkJoinWorkerThreadFactory实现,null如果ForkJoinPool已经有一个活的工作人员(这可以防止池创建多个工作人员),它就会返回。Random即使工作线程关闭并再次返回,我也确保我的代码返回相同的实例。
  • 具有不确定迭代顺序的集合,例如HashMapHashSet导致元素在每次运行时以不同的顺序抓取随机数。我通过使用LinkedHashMap和纠正了这个问题LinkedHashSet
  • 具有非确定性 hashCode() 实现的对象,例如Enum.hashCode(). 我忘记了这导致了什么问题,但我通过自己计算 hashCode() 而不是依赖内置方法来纠正它。

这是 ForkJoinWorkerThreadFactory 的示例实现:

class MyForkJoinWorkerThread extends ForkJoinWorkerThread
{
    MyForkJoinWorkerThread(ForkJoinPool pool)
    {
        super(pool);
        // Change thread name after ForkJoinPool.registerWorker() does the same
        setName("DETERMINISTIC_WORKER");
    }
}

ForkJoinWorkerThreadFactory factory = new ForkJoinWorkerThreadFactory()
{
    private WeakReference<Thread> currentWorker = new WeakReference<>(null);

    @Override
    public synchronized ForkJoinWorkerThread newThread(ForkJoinPool pool)
    {
        // If the pool already has a live thread, wait for it to shut down.
        Thread thread = currentWorker.get();
        if (thread != null && thread.isAlive())
        {
            try
            {
                thread.join();
            }
            catch (InterruptedException e)
            {
                log.error("", e);
            }
        }
        ForkJoinWorkerThread result = new MyForkJoinWorkerThread(pool);
        currentWorker = new WeakReference<>(result);
        return result;
    }
};
于 2016-01-14T15:32:11.273 回答
1

主线程始终是您的应用程序将创建的第一个线程。因此,当您创建ForkJoinPoolwith parallelismof 时1,您正在创建另一个线程。现在应用程序中实际上将有两个线程(因为您创建了一个pool线程)。

如果您只需要一个主线程,您可以按顺序执行您的代码(而不是并行执行)。

于 2015-12-01T05:16:32.230 回答