如何配置单线程ForkJoinPool?

时间:2015-12-01 03:34:21

标签: java multithreading fork-join forkjoinpool

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

我正在执行在Random内调用ForkJoinPool的代码。每次运行时,我都会遇到不同的运行时行为,因此很难调查回归。

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

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

2 个答案:

答案 0 :(得分:7)

所以,事实证明我错了。

配置ForkJoinPool并将parallelism设置为1时,只有一个线程执行任务。 main线程在ForkJoin.get()上被阻止。它实际上并没有执行任何任务。

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

    如果工作线程空闲得足够长,
  • ForkJoinPool正在使用不同的工作线程(具有不同的名称)执行任务。例如,如果主线程在调试断点上挂起,则工作线程将变为空闲并关闭。当我恢复执行时,ForkJoinThread将启动一个具有不同名称的新工作线程。为了解决这个问题,我不得不provide a custom ForkJoinWorkerThreadFactory implementation that returns null if the ForkJoinPool already has a live worker(这可以防止池创建多个工作者)。即使工作线程关闭并再次返回,我也确保我的代码返回相同的Random实例。
  • 具有非确定性迭代顺序的集合(例如HashMapHashSet)导致元素在每次运行时以不同的顺序获取随机数。我使用LinkedHashMapLinkedHashSet
  • 更正了此问题
  • 具有非确定性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;
    }
};

答案 1 :(得分:0)

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

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