1. シンプルで粗雑なスレッド
最も独創的な方法は、タスクを並列または非同期で実行したい場合、以下に示すようにスレッドを開始するメソッドを直接使用します。毎回新しいスレッドを使用するこの方法には、次のような多くの欠点があります:
新しいスレッドのたびに新しいオブジェクトを作成するパフォーマンスが低い
スレッドは統一された管理に欠けており、新しいスレッドが制限なく作成され、相互に競合します。また、システム リソースを占有しすぎて、クラッシュや OOM (メモリ不足) が発生する可能性もあります。
には、スケジュールされた実行、定期的な実行、スレッドの中断などの機能が不足しています。
2. スレッド プール
これらの問題を解決するために、Jdk1.5 以降に java.util.concurrent パッケージが追加されました。このパッケージは主に Java でのスレッドとスレッド プールの使用を紹介します。これは、開発中のスレッドの問題に対処するのに非常に役立ちます。
1. 機能
システム環境に応じて、スレッドの数を自動または手動で設定して、最適な運用効果を実現できます。スレッドの数が少ないとシステム リソースが無駄になり、多いとシステムの輻輳と非効率が発生します。スレッド プールを使用してスレッドの数を制御すると、他のスレッドはキューに入れられて待機します。タスクが実行されると、キュー内の最前部のタスクが取得され、実行が開始されます。キュー内に待機中のプロセスがない場合、スレッド プールのこのリソースは待機中になります。新しいタスクを実行する必要がある場合、スレッド プールに待機中のワーカー スレッドがあれば実行を開始できます。それ以外の場合は、待機キューに入ります。
2. スレッド プールを使用する理由
既存のスレッドを再利用し、オブジェクトの作成と破棄のコストを削減し、優れたパフォーマンスを実現します。
同時スレッドの最大数を効果的に制御し、システムリソースの使用率を改善し、過度のリソースの競合と輻輳を回避できます。
スケジュール実行、定期実行、シングルスレッド、同時実行制御などの機能を提供します。
システムの容量に応じてスレッド プール内の作業スレッドの数を調整して、過度のメモリ消費によるサーバーの枯渇を防ぐことができます (各スレッドには約 1MB のメモリが必要です。オープンされるスレッドが増えるほど、より多くのメモリが必要になります)メモリが消費され、最終的にはクラッシュします)。
3. 主なクラス
Javaにおけるスレッドプールの最上位インターフェースはExecutorですが、厳密に言えばExecutorはスレッドプールではなく、スレッドを実行するための単なるツールです。実際のスレッド プール インターフェイスは ExecutorService です。
スレッド プールの設定は比較的複雑で、特にスレッド プールの原理があまり明確でない場合は、設定されたスレッド プールが最適ではない可能性が非常に高いため、 Executors クラス いくつかの静的ファクトリーを作成し、一般的に使用されるいくつかのスレッド プールを生成します。
1) newSingleThreadExecutor
シングルスレッドのスレッドプールを作成します。このスレッド プールには 1 つのスレッドだけが動作しており、これはすべてのタスクを直列に実行する単一のスレッドと同等です。唯一のスレッドが異常終了した場合、新しいスレッドがそれを置き換えます。このスレッド プールにより、すべてのタスクが送信された順序で実行されることが保証されます。
2) newFixedThreadPool
固定サイズのスレッドプールを作成します。スレッドは、スレッド プールの最大サイズに達するまで、タスクが送信されるたびに作成されます。スレッド プールのサイズは、最大値に達しても変更されません。実行例外によってスレッドが終了すると、スレッド プールは新しいスレッドで補充されます。
3) newCachedThreadPool
キャッシュ可能なスレッドプールを作成します。スレッド プールのサイズがタスクの処理に必要なスレッドを超える場合、
タスクの数が増加すると、一部のアイドル スレッド (60 秒間タスクが実行されない) がリサイクルされます。このスレッド プールは新しいスレッドをインテリジェントに追加できます。プロセスタスク。このスレッド プールは、スレッド プールのサイズを制限しません。スレッド プールのサイズは、オペレーティング システム (または JVM) が作成できる最大スレッド サイズに完全に依存します。
4) newScheduledThreadPool
サイズ無制限のスレッドプールを作成します。このスレッド プールは、タスクのタイミングと定期的な実行をサポートします。
3. 例
1) newCachedThreadPool
キャッシュ可能なスレッド プールを作成します。スレッド プールの長さが処理の必要性を超えた場合、リサイクルする方法がない場合は、新しいスレッドを作成します。サンプルコードは以下の通りです:new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub 这里放你要执行的方法 } }).start();
2) newFixedThreadPool
同時スレッドの最大数を制御するために固定長のスレッドプールを作成し、過剰なスレッドはキューで待機します。サンプルコードは以下の通りです。package test; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExecutorTest { public static void main(String[] args) { ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { final int index = i; try { Thread.sleep(index * 1000); } catch (InterruptedException e) { e.printStackTrace(); } cachedThreadPool.execute(new Runnable() { public void run() { System.out.println(index); } }); } } }
3)newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码如下:
package test; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class ThreadPoolExecutorTest { public static void main(String[] args) { ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); scheduledThreadPool.schedule(new Runnable() { public void run() { System.out.println("delay 3 seconds"); } }, 3, TimeUnit.SECONDS); } }
表示延迟3秒执行。
定期执行示例代码如下:
package test; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class ThreadPoolExecutorTest { public static void main(String[] args) { ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); scheduledThreadPool.scheduleAtFixedRate(new Runnable() { public void run() { System.out.println("delay 1 seconds, and excute every 3 seconds"); } }, 1, 3, TimeUnit.SECONDS); } }
表示延迟1秒后每3秒执行一次。
4)newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。示例代码如下:
package test; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExecutorTest { public static void main(String[] args) { ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { final int index = i; singleThreadExecutor.execute(new Runnable() { public void run() { try { System.out.println(index); Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }); } } }
结果依次输出,相当于顺序执行各个任务。
注意:以上的execute()方法可以替换为submit()方法,执行的结果是一样的。
四、submit()和execute()的区别
JDK5往后,任务分两类:一类是实现了Runnable接口的类,一类是实现了Callable接口的类。两者都可以被ExecutorService执行,它们的区别是:
execute(Runnable x) 没有返回值。可以执行任务,但无法判断任务是否成功完成。——实现Runnable接口
submit(Runnable x) 返回一个future。可以用这个future来判断任务是否成功完成。——实现Callable接口