スレッドはしばしば仕事に関与しています。たとえば、一部のタスクは、しばしば非同期実行のためにスレッドに引き渡されます。または、サーバープログラムは、リクエストごとに個別のスレッド処理タスクを確立します。使用するデータベース接続など、スレッドを超えて。これらの作成、破壊、または開閉操作と閉鎖操作は、システムのパフォーマンスに大きな影響を与えます。したがって、「プール」の使用が強調されています。
1.スレッドプールを使用する理由
セクション3.6.1で説明した実装では、新しいワーカースレッドが各クライアントに割り当てられます。ワーカースレッドがクライアントと通信すると、スレッドが破壊されます。この実装方法には、次の欠点があります。
•サーバーの作成と破壊のオーバーヘッド(時間とシステムのリソースを含む)は非常に高いです。このアイテムは説明する必要はありません。「スレッド作成プロセス」を確認できます。マシン自体が行った作業に加えて、スタックリソースの占有率がすべて必要であることもインスタンス化して開始する必要があります。
•スレッドの作成と破壊のオーバーヘッドに加えて、アクティブスレッドもシステムリソースを消費します。これは、スタックリソースの消費である必要があります。また、データベース接続の数を推測するとも考えられます。
•スレッドの数が固定されており、各スレッドに長い宣言サイクルがある場合、スレッドスイッチングも比較的固定されています。異なるオペレーティングシステムには、通常は20ミリ秒前後のスイッチングサイクルが異なります。ここで説明したスイッチは、JVMのスケジューリングに基づくスレッド間でCPU使用権を転送することと、基礎となるオペレーティングシステムです。スレッドが頻繁に作成されて破壊される場合、スレッドが破壊された後、使用権を準備するためにスレッドを実行する必要があるため、スレッドは頻繁に切り替えられます。この場合、スレッド間の切り替えは、システムの固定されたスイッチングサイクルに従わず、切り替えスレッドのオーバーヘッドは作成と破壊のオーバーヘッドよりもさらに大きくなります。
比較的言えば、スレッドプールを使用して、いくつかのスレッドが事前に作成されているため、作業キューからタスクを絶えず削除してからタスクを実行します。ワーカースレッドが1つのタスクを完了すると、作業キューで別のタスクを実行し続けます。利点は次のとおりです。
•作成と破壊の数を減らし、各ワーカースレッドを常に再利用でき、複数のタスクを実行できます。
•スレッドプールのスレッドの数は、システムの過剰なリソースのためにシステムがクラッシュするのを防ぐためのシステムのキャリング容量に従って簡単に調整できます。
2。スレッドプールの簡単な実装
以下は私自身によって書かれたシンプルなスレッドプールで、Javaネットワークプログラミングブックからも直接描かれました
パッケージスレッド; java.util.linkedlist;/***通常のスレッドプールの長さ、最大長、キューの長さに基づいて、スレッドプールの実装* @author han*/public class mythreadpoolはスレッドグループを拡張することができます{// //プライベートブールを閉じるかどうかisclosed = false; //キュープライベートLinkedList <Runnable> workqueue; //スレッドプールIDプライベート静的int threadpoolid; private int threadid; Public Mythreadpool(int poolsize){super( "mythreadpool。"+threadpoolid); ThreadPoolid ++; setdaemon(true); workqueue = new linkedlist <runnable>(); for(int i = 0; i <poolsize; i ++){new workthread()。start(); }} //ここでは、同期されたパブリック同期void execute(runnableタスク)を使用する効率を回避するために、concurrentlinkedqueueに変更できます。 } else {workqueue.add(task); notify(); }}保護された同期runnable gettask()throws arturnedexception {while(workqueue.size()== 0){if(isclosed){return null; } 待って(); } return workqueue.removefirst(); } public synchronized void close(){if(!isclosed){isclosed = true; workqueue.clear();割り込み(); }} public void join(){synchronized(this){isclosed = true; notifyall(); } thread []スレッド= newスレッド[ActiveCount()]; int count = eNumate(swrets); for(int i = 0; i <count; i ++){try {threads [i] .jein(); } catch(Exception e){}}} class workthread extends thread {public workthread(){super(mythreadpool.this、 "workthread"+(threadid ++)); system.out.println( "create ..."); } @Override public void run(){while(!is intertrupted()){system.out.println( "run ..");実行可能なタスク= null; try {//これはブロッキングメソッドタスク= getTask(); } catch(例外e){} if(task!= null){task.run(); } else {break; }}}}}}このスレッドプールは、主に作業キューといくつかの事前に作成されたスレッドを定義します。実行メソッドが呼び出されている限り、タスクをスレッドに送信できます。
タスクがない場合、後続のスレッドは、新しいタスクが入って目覚めるまでgetTask()でブロックされます。
結合と閉じる両方を使用して、スレッドプールを閉じることができます。違いは、結合がキューのタスクを完了し、クローズがすぐにキューをクリアし、すべてのワーカースレッドを中断することです。 close()の割り込み()は、スレッドグループに子スレッドを含むそれぞれの割り込み()を呼び出すことと同等です。したがって、スレッドが待っているか眠っている場合、中断のexceptionがスローされます。
テストクラスは次のとおりです。
Public Class testMyThreadPool {public static void main(String [] args)throws arturtedexception {mythreadpool pool = new mythreadpool(3); for(int i = 0; i <10; i ++){pool.execute(new runnable(){@override public void run(){try {shood.sleep(1000);} catch(arternedexception e){} system.out.println( "working ...");}}); } pool.join(); //pool.close(); }}3。JDKクラスライブラリが提供するスレッドプール
Javaは、優れたスレッドプールの実装を提供します。これは、私たち自身の実装よりも堅牢で効率的であり、より強力な機能を備えています。
クラス図は次のとおりです。
高齢者はすでにこのタイプのスレッドプールについて良い説明をしています。 Baiduの下のJavaスレッドプールには、非常に詳細な例とチュートリアルが書かれているので、ここでは繰り返しません。
4。スプリングインジェクションスレッドプール
Spring Frameworkを使用する場合、Javaが提供する方法を使用してスレッドプールを作成する場合、マルチスレッドアプリケーションで管理するのは非常に不便であり、Springを使用するという考えに準拠していません。 (春は静的な方法で注入できますが)
実際、Spring自体は、スレッドプールの適切な実装も提供します。このクラスは、threadpooltaskexecutorと呼ばれます。
春の構成は次のとおりです。
<bean id = "executorservice"> <プロパティ名= "corepoolsize" value = "$ {threadpool.corepoolsize}" /> <! - スレッドプールによって維持されるスレッドの最小数 - > <プロパティname = "keepaliveseconds" value = "$ {swrehpool.keepaliveseconds}" name = "maxpoolsize" value = "$ {threadpool.maxpoolsize}" /> <! - スレッドプールによって維持されるスレッドの最大数 - > <プロパティname = "queuecapacity" value = "{threadpool.queuecapacity}" /> <5。スレッドプールの使用に関するメモ
•デッドロック
マルチスレッドプログラムは、デッドロックのリスクがあります。最も簡単なケースは、2つのスレッドAB、Holds Lock 1、要求ロック2、Bがロック2を保持し、ロック1を要求することです(この状況はMySQL Exclusive Lockにも表示され、データベースはエラーメッセージを直接報告します)。スレッドプールには別のデッドロックがあります。スレッドプール内のすべてのワーカースレッドがそれぞれのタスクを実行しているときにブロックされていると仮定し、特定のタスクAの実行結果を待っています。タスクAはキューにあり、アイドルスレッドがないため実行できません。このようにして、スレッドプールのすべてのリソースがブロックされ、デッドロックが生成されます。
•システムリソースが不十分です
スレッドプールのスレッドの数が非常に多い場合、これらのスレッドは、メモリやその他のシステムリソースを含む大量のリソースを消費し、システムパフォーマンスに深刻な影響を与えます。
•同時エラー
スレッドプールの作業キューは、ワーカーがタイムリーにタスクを取得できるようにするためのWait()およびNotify()メソッドに依存していますが、これら2つの方法は使用が困難です。コードが間違っている場合、通知が失われる可能性があり、作業キューで処理する必要があるタスクを無視して、ワーカースレッドをアイドル状態に保ちます。より成熟したスレッドプールを使用するのが最善だからです。
•スレッドリーク
スレッドプールを使用する深刻なリスクは、糸の漏れです。ワーカースレッドの固定数のスレッドプールの場合、ワーカースレッドがタスクを実行するときにRuntimeExceptionまたはエラーをスローする場合、およびこれらの例外またはエラーがキャッチされない場合、ワーカースレッドは異常に終了し、スレッドプールが永久にスレッドを失います。 (これはとても面白いです)
別の状況は、タスクを実行するときにワーカースレッドがブロックされることです。ユーザーの入力データを待っているが、ユーザーがデータを入力しないため、常にスレッドがブロックされます。このようなワーカースレッドは名前のみであり、実際にはタスクを実行しません。スレッドプール内のすべてのスレッドがこの状態にある場合、スレッドプールは新しいタスクを追加できません。
•タスク過負荷
ワーカースレッドキューで実行されるようにキューに巻き込まれた多数のタスクがある場合、これらのタスク自体がシステムリソースを消費しすぎてリソース不足を引き起こす可能性があります。
要約すると、スレッドプールを使用する場合、次の原則に従う必要があります。
1.タスクAが実行中にタスクBの実行結果を同期して待機する必要がある場合、タスクAはスレッドプールの作業キューに追加するのに適していません。他のタスクがキューに追加されるような結果を実行するのを待つ必要がある場合、それはデッドロックを引き起こす可能性があります
2.タスクがブロックされ、長時間ブロックされている場合は、ワーカースレッドの永続的なブロッキングとスレッドリークの原因を避けるために、タイムアウト時間を設定する必要があります。サーバープログラムでは、クライアントがクライアントから送信したデータを接続または待機するスレッドが待機している場合、閉塞を引き起こす可能性があります。次の方法で時間を設定できます。
SetSoTimeOutのSETSOTIMEOUTメソッドを呼び出して、クライアントが接続するのを待つタイムアウト時間を設定します。
顧客に接続されているソケットごとに、ソケットのSetSoTimeOutメソッドを呼び出して、顧客がデータを送信するのを待っているタイムアウト時間を設定します。
3.タスクの特性を理解し、タスクがIO操作をブロックすることが多い操作を実行するか、ブロックしない操作を実行するかどうかを分析します。前者はCPUを断続的に占有し、後者はより高い利用率を持っています。タスクを完了するのにどれくらい時間がかかりますか?短期的なタスクですか、それとも長期的なタスクですか?次に、タスクの特性に従ってタスクを分類し、異なるスレッドプールの作業キューにさまざまなタイプのタスクを追加します。このようにして、各スレッドプールは、タスクの特性に従って割り当てて調整できます。
4.スレッドプールのサイズを変更します。スレッドプールの最適なサイズは、主にシステム内の利用可能なCPUの数と、作業キュー内のタスクの特性に依存します。 N CPUを備えたシステムに1つの作業キューがあり、それらがすべて算術性のあるタスク(ブロッキングではない)である場合、スレッドプールにnまたはn+1のワーカースレッドがある場合、最大CPU使用量が一般的に取得されます。
作業キューにIO操作を実行してブロックするタスクが含まれている場合、すべての作業スレッドが常に動作しているわけではないため、スレッドプールサイズを利用可能なCPUの数を超えるようにします。典型的なタスクを選択し、CPUを実際に占有する時間とこのタスクを実行するプロジェクトで操作を実行する時間との比率を推定します。 N CPUを備えたシステムの場合、CPUが完全に利用されることを確認するために、約N*(1+wt/ST)スレッドを設定する必要があります。
もちろん、スレッドプールを調整するプロセスで考慮すべき唯一のものではありません。スレッドプールの作業の数が増加するにつれて、ソケット、オープンファイルハンドル、データベース接続など、メモリまたはその他のリソース制限も発生します。マルチスレッドで消費されたシステムリソースがシステムの持久力の範囲内であることを確認する必要があります。
5.タスクの過負荷を避けてください。サーバーは、システムのキャリング容量に基づいて、顧客の同時接続の数を制限する必要があります。クライアントの接続が制限値を超えると、サーバーは接続を拒否し、フレンドリーなプロンプトを作成するか、キューの長さを制限できます。
上記のいくつかの実装方法とJavaスレッドプールに対する回答は、私があなたと共有したすべてのコンテンツです。参照を提供できることを願っています。wulin.comをもっとサポートできることを願っています。