この記事では、主に Java Executor フレームワークの例の詳細な説明に関する関連情報を紹介します。ここでは、この部分の内容を学習して理解するのに役立つ例を提供します。
例の詳細な説明。 Java Executor フレームワークの
ほとんどの同時実行性はすべてタスクの実行によって実現されます。
タスクを実行するには、通常、シリアルとパラレルの 2 つの方法があります。
class SingleThreadWebServer { public static void main(String[] args) throws Exception { ServerSocket socket = new ServerSocket(80); while(true) { Socket conn = socket.accept(); handleRequest(conn); } } } class ThreadPerTaskWebServer { public static void main(String[] args) throws Exception { ServerSocket socket = new ServerSocket(80); while(true) { final Socket conn = socket.accept(); Runnable task = new Runnable() { public void run() { handleRequest(conn); } }; new Thread(task).start(); } } }
もちろん、上記のどちらの方法にも問題があります。シングルスレッドの問題は、マルチスレッドバージョンでは同時実行の量がボトルネックとなり、無制限にスレッドを作成するとリソースが不足してしまうことです。
Executor Framework
タスクは一連の論理的な作業単位であり、スレッドはタスクを非同期に実行できるようにするメカニズムです。
JDK は Executor インターフェイスを提供します:
public interface Executor { void execute(Runnable command); }
Executor インターフェイスは比較的単純ですが、これは非同期タスク実行フレームワークの基礎であり、さまざまなタイプのタスク実行戦略をサポートできます。これは、タスク送信プロセスを実行プロセスから分離する標準的な方法を提供します。 Runnable を使用してタスクを表現します。 Executor の実装により、ライフサイクル サポートや統計アプリケーション管理などのメカニズムが提供されます。
Executor は、プロデューサー/コンシューマー モデルに基づいており、タスクを送信する操作はプロデューサーに相当し、タスクを実行するスレッドはコンシューマーに相当します。
Executor ベースの WebServer の例は次のとおりです:
public class TaskExecutorWebServer { private static final int NTHREADS = 100; private static final Executor exec = Executors.newFixedThreadPool(NTHREADS); public static void main(String[] args) throws Exception { ServerSocket serverSocket = new ServerSocket(80); while (true) { final Socket conn = serverSocket.accept(); Runnable task = new Runnable() { @Override public void run() { handleRequest(conn); } }; exec.execute(task); } } }
さらに、次のコードのように Executor を自分で実装して、同時か並列かを制御することもできます:
/** * 执行已提交的 Runnable 任务的对象。 * 此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法。 * 通常使用 Executor 而不是显式地创建线程。 * * * @author renchunxiao * */ public class ExecutorDemo { public static void main(String[] args) { Executor executor = new ThreadExecutor(); executor.execute(new Runnable() { @Override public void run() { // do something } }); Executor executor2 = new SerialExecutor(); executor2.execute(new Runnable() { @Override public void run() { // do something } }); } } /** * 创建一个线程来执行 command * * @author renchunxiao * */ class ThreadExecutor implements Executor { @Override public void execute(Runnable command) { new Thread(command).start(); } } /** * 串行执行 command * * @author renchunxiao * */ class SerialExecutor implements Executor { @Override public void execute(Runnable command) { command.run(); } }
スレッド プール
スレッド プールはスレッド プールのリソースであり、Executor の静的ファクトリ メソッドを通じてスレッド プールを作成できます。
新しい固定スレッドプール。固定長のスレッド プールを作成し、スレッド プールの最大数に達するまで、タスクが送信されるたびにスレッドを作成します。
新しいSingleThreadExecutor。シングルスレッドプール。
新しいキャッシュされたスレッドプール。タスクのサイズに応じて拡張するスレッド プール。
新しいスケジュールされたスレッドプール。固定長のスレッド プールを作成して、タスクを遅延または時間指定して実行します。
JVM は、すべての非デーモン スレッドが終了した後にのみ終了します。そのため、Executor を正しく閉じることができない場合、JVM は終了できません。
実行サービスのライフサイクル問題を解決するために、Executor インターフェイスを拡張する新しいインターフェイス ExecutorService があります。
public interface ExecutorService extends Executor { void shutdown(); List<Runnable> shutdownNow(); boolean isShutdown(); boolean isTerminated(); boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException; <T> Future<T> submit(Callable<T> task); <T> Future<T> submit(Runnable task, T result); Future<?> submit(Runnable task); <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException; <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException; <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException; <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
ExecutorService ライフサイクルには、実行中、クローズ済み、終了済みの 3 つの状態があります。 ExecutorService は、最初に作成されたときに実行されています。 shutdown メソッドは正常にシャットダウンします。新しいタスクは受け入れられなくなり、既に実行されたタスク (まだ開始されていないタスクを含む) が完了するまで待機します。 shutdownNow メソッドは、大まかなシャットダウンを実行します。実行中のすべてのタスクのキャンセルを試行し、キュー内のまだ開始されていないタスクは開始しません。すべてのタスクが完了すると、終了状態になります。
Callable と Future
Executor フレームワークは、基本的なタスク表現として Runnable を使用します。 Runnable は、run メソッドが値を返すことができず、チェック例外をスローするという点で、限定された抽象化です。
データベースのクエリやネットワークからのリソースの取得など、多くのタスクは実際には遅延を伴う計算です。これらのタスクでは、Callable の方が優れた抽象化であり、呼び出しが値を返し、例外をスローする可能性があることを前提としています。
Executor によって実行されるタスクには、作成、送信、開始、完了という 4 つのライフサイクル ステージがあります。一部のタスクには時間がかかり、キャンセルが必要になる場合があるため、Executor フレームワークでは、送信されたものの開始されていないタスクをキャンセルできます。
Future はタスクのライフサイクルを表し、タスクが完了したかキャンセルされたかを判断したり、タスクの結果を取得したりタスクをキャンセルしたりするための対応するメソッドを提供します。
以上がJava の Executor フレームワークの例の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。