JavaアプリケーションのFork / Joinへの移行、または覚えておくべきこと

JDKの7番目のバージョンのリリースにより、私たち幸せなJava開発者は、 Fork / Joinフレームワークから利用できるようになりました 。 APIに関するフレームワークは、すでによく知られているExecutorServiceに非常に似ていますが、非常に具体的なパフォーマンスの向上とスレッドの実際の「軽さ」を提供します。



ここで、 Fork / Joinに切り替えるときに注意すべき点を検討します。





スレッドローカル変数



ExecutorServiceでは、最初から最後まで1つのタスクが単一のスレッドで実行されることが保証されていました。

Fork / Joinでは、スレッド化に大きな変更が加えられました。 タスク( ForkJoinTask )には、スレッドとは異なるセマンティクスがあります。1つのスレッドは、時間的に交差する複数のタスクを実行できます。



たとえば、 task.invoke()を呼び出すとき、元のタスクが1つのスレッドによって実行され、同じスレッドが新しいタスクtaskの実行を開始した場合、シナリオは非常に可能です。 これは高速です。別のスレッドを開始する必要はなく、コンテキストの切り替えは回避されます。 タスクの終了後、元のタスクは実行を継続しました。



したがって、ローカルフロー変数を使用する方法を検討する必要があります。

ThreadLocalは、いくつかの場合に使用できます。

  1. スレッドセーフではないユーティリティクラスを格納します。 たとえば、 SimpleDateFormat 。 その作成は非常に高価であり、複数のスレッドを使用すると、誤った操作や例外が発生します。
  2. スレッド実行コンテキストを保存します。 たとえば、現在のトランザクション、データベースへの接続など。または、メソッドシグネチャまたはセッターではなく、ストリームのローカルコンテキストを介して転送することを単に決定したデータ。 たとえば、 Struts2のActionContextです


最初のケースでFork / Joinに切り替えたときにひどいことが起こらない場合、2番目のケースでは、1つのタスクのローカル変数が別のタスクで使用可能になります。



道徳 :この場合ThreadLocal変数を使用しないでください。または、 ForkJoinTaskをサポートするThreadLocalアナログを実装しないでください。



ロック



一般に、フレームワークは、ロックおよび同期の他の手段の使用に制限を課しません。 さらに、他のスレッドを待機しているときにスレッドがハングする状況を回避するのに役立ちます( スレッド不足 )。



たとえば、プールサイズが上から制限されたThreadPoolExecutorがあります。 簡単にするために、これを1つとしましょう。 1つのスレッドを開始し、次に別のスレッドをキューに追加して、完了するのを待ちます。 この場合、両方のフローを待つことはありません。 プールが大きい場合、他のスレッドが互いに待機しており、デッドロック状態にある場合を考慮することができます。 または、さらに簡単に、1つのタスクが2つ目、3つ目などを生成し、誰もが実行中のタスクの結果を待っています。



問題の一部は、 join()が本質的にスレッドをプールに戻すという事実によって解決されます。



必要なレベルの並行性を確保するために、 ForkJoinPoolは制御されたロックメカニズムを提供します。 ForkJoinPool.ManagedBlockerクラスを使用して、スレッドがロック待機をブロックできることをForkJoinPool 'yに伝えることができ、 ForkJoinPoolは追加のスレッドを作成して指定されたレベルの並列処理を提供します。



コードでReentrantLockを使用するとします。 ForkJoinPool.ManagedBlockerインターフェースを次のように実装する必要があります(javadocsから取得)。



class ManagedLocker implements ManagedBlocker { final ReentrantLock lock; boolean hasLock = false; ManagedLocker(ReentrantLock lock) { this.lock = lock; } public boolean block() { if (!hasLock) lock.lock(); return true; } public boolean isReleasable() { return hasLock || (hasLock = lock.tryLock()); } }
      
      





コードで次のようにロックを使用します。

 ReentrantLock lock = new ReentrantLock(); //Somewhere in thread try{ ForkJoinPool.managedBlock(new ManagedLocker(lock)); //Guarded code goes here }finally{ lock.unlock(); }
      
      







それだけです



そして、力があなたと共に来ますように!



All Articles