スレッドの安全性はExecutorService
保証されていますか?
異なるスレッドから同じ ThreadPoolExecutor にジョブを送信しますが、タスクをやり取り/送信する前にエグゼキューターへのアクセスを同期する必要がありますか?
スレッドの安全性はExecutorService
保証されていますか?
異なるスレッドから同じ ThreadPoolExecutor にジョブを送信しますが、タスクをやり取り/送信する前にエグゼキューターへのアクセスを同期する必要がありますか?
(他の回答とは異なり)スレッドセーフ契約が文書化されています:(interface
メソッドのjavadocではなく)javadocsを調べてください。たとえば、ExecutorServicejavadocの下部には次のものがあります。
メモリの一貫性の影響:RunnableまたはCallableタスクをExecutorServiceに送信する前のスレッド内のアクションは、 そのタスクによって実行されるアクション の前に発生します。アクションは、Future.get()を介して結果が取得される前に発生します。
これはこれに答えるのに十分です:
「タスクを操作/送信する前に、エグゼキュータへのアクセスを同期する必要がありますか?」
いいえ、しません。ExecutorService
外部同期を使用せずに、ジョブを作成して(正しく実装された)任意のユーザーに送信することは問題ありません。これは、主要な設計目標の1つです。
ExecutorService
は並行ユーティリティです。つまり、パフォーマンスのために、同期を必要とせずに最大限に動作するように設計されています。(同期によりスレッドの競合が発生し、マルチスレッドの効率が低下する可能性があります。特に、多数のスレッドにスケールアップする場合はそうです。)
将来、タスクがいつ実行または完了するかについての保証はありません(タスクを送信したのと同じスレッドですぐに実行される場合もあります)が、ワーカースレッドは、送信スレッドが実行したすべての効果を確認したことが保証されます。提出のポイント。したがって、(実行するスレッド)タスクは、同期、スレッドセーフクラス、またはその他の形式の「安全な公開」なしで、その使用のために作成されたデータを安全に読み取ることもできます。タスクを送信するという行為自体は、入力データをタスクに「安全に公開」するのに十分です。タスクの実行中に入力データが変更されないようにする必要があります。
同様に、を介してタスクの結果をフェッチするFuture.get()
と、取得スレッドは、エグゼキュータのワーカースレッドによって行われたすべての効果を確認することが保証されます(返された結果と、ワーカースレッドが行った可能性のある副作用の変更の両方で) 。
この契約は、タスク自体がより多くのタスクを送信することも問題ないことも意味します。
「ExecutorServiceはスレッドセーフを保証しますか?」
さて、質問のこの部分ははるかに一般的です。たとえば、メソッドに関するスレッドセーフコントラクトのステートメントは見つかりませんでした shutdownAndAwaitTermination
。ただし、Javadocのコードサンプルでは同期が使用されていないことに注意してください。(おそらく、シャットダウンは、たとえばワーカースレッドではなく、エグゼキューターを作成したのと同じスレッドによって引き起こされるという隠された仮定がありますか?)
ところで、並行プログラミングの世界をしっかりと理解するために、「JavaConcurrencyInPractice」という本をお勧めします。
あなたの質問はかなり自由です。ExecutorService
インターフェースが行うことは、どこかのスレッドが送信されたRunnable
またはCallable
インスタンスを処理することを保証することだけです。
送信されたRunnable
/が、他の/ s インスタンスCallable
からアクセスできる共有データ構造を参照している場合 (異なるスレッドによって同時に処理される可能性があります)、このデータ構造全体でスレッド セーフを確保するのはユーザーの責任です。Runnable
Callable
質問の 2 番目の部分に答えるには、はい、タスクを送信する前に ThreadPoolExecutor にアクセスできます。例えば
BlockingQueue<Runnable> workQ = new LinkedBlockingQueue<Runnable>();
ExecutorService execService = new ThreadPoolExecutor(4, 4, 0L, TimeUnit.SECONDS, workQ);
...
execService.submit(new Callable(...));
編集
ブライアンのコメントに基づいて、私があなたの質問を誤解した場合: 複数のプロデューサー スレッドから へのタスクの送信は、ExecutorService
通常、スレッド セーフになります (私が知る限り、インターフェイスの API で明示的に言及されていないにもかかわらず)。スレッド セーフを提供しない実装は、マルチスレッド環境では役に立ちません (複数のプロデューサー/複数のコンシューマーはかなり一般的なパラダイムであるExecutorService
ためjava.util.concurrent
)。
ThreadPoolExecutor
答えはイエスです。 すべての実装がスレッドセーフであることを強制または保証するものではなく、インターフェイスであるため保証できませExecutorService
ん。これらのタイプのコントラクトは、Java インターフェースの範囲外です。ただし、ThreadPoolExecutor
どちらもスレッドセーフであると明確に文書化されています。さらに、すべての実装がスレッドセーフであることを要求するインターフェースであるThreadPoolExecutor
ジョブキューを使用して管理します。の実装はjava.util.concurrent.BlockingQueue
すべてスレッドセーフであると安全に想定できます。非標準の実装はそうではないかもしれませんが、誰かがスレッドセーフではない実装キューを提供した場合、それはまったくばかげています。java.util.concurrent.*
BlockingQueue
BlockingQueue
したがって、タイトルの質問に対する答えは明らかにyesです。2 つの間にいくつかの不一致があるため、質問の後続の本文への回答はおそらくです。