それで、私が間違っていたことがわかりました。
を 1 に設定して構成するForkJoinPool
とparallelism
、1 つのスレッドのみがタスクを実行します。main
スレッドは でブロックされていますForkJoin.get()
。実際にはタスクを実行しません。
とはいえ、決定論的な動作を提供するのは本当に難しいことがわかりました。ここに私が修正しなければならなかったいくつかの問題があります:
ForkJoinPool
ワーカースレッドが十分長くアイドル状態になった場合、別のワーカースレッド (別の名前) を使用してタスクを実行していました。たとえば、メイン スレッドがデバッグ ブレークポイントで中断された場合、ワーカー スレッドはアイドル状態になり、シャットダウンされます。実行を再開するForkJoinThread
と、別の名前の新しいワーカー スレッドがスピンアップします。これを解決するために、既に稼働中のワーカーがある場合に返すカスタム実装を提供する必要ForkJoinWorkerThreadFactory
null
ForkJoinPool
がありました(これにより、プールが複数のワーカーを作成するのを防ぎます)。Random
また、ワーカー スレッドがシャットダウンして再び戻ってきた場合でも、コードが同じインスタンスを返すようにしました。
HashMap
またはHashSet
実行ごとに異なる順序で乱数を取得する要素につながるなど、非決定論的な反復順序を持つコレクション。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;
}
};