前書き
私が週末に水グループにいたとき、私は小さなパートナーがオンラインの問題に遭遇したことに気づきました
スレッドプールでは、1つのスレッドステータスのみが実行可能で、他のスレッドステータスは待機中です。考えられる原因は何ですか。
スレッドプールには25のスレッドがあり、1つのスレッドのみがネットワーク読み取りでスタックし、ステータスはRUNNABLEであり、他のスレッドはWAITINGです。
このツールを使用したことがない友人もいるかもしれません。このパフォーマンス監視ツールJMCを簡単に紹介します。JMCはJRockitJVMから派生した監視および管理ツールのセットです。OracleはJAVA7u4(Java 7 Update 40)のリリースにこのツールを含めました。ユーザーは個別にダウンロードする必要がなくなりました
コマンドでjmcを実行するだけです
アプリケーションの起動構成パラメーターは次のとおりです
-Dcom.sun.management.jmxremote.port=7091
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
構成済みのJMCに接続して、さまざまな検出インジケーターを確認します。
もともと、この小さなパートナーにコードを送って見てもらいたかったのですが、彼は銀行のプロジェクトをやっていてインターネットに接続していないので、携帯電話を使ってコンピューターにビデオを開くことしかできなかったと言いました概要を教えてください。このコードのシーンを復元します。冗長なコードを省略し、問題の原因となるコードのみを残しているため、多くの小さなパートナーが一度に問題を見つけることができると推定されます。
public class BankDemo {
public ExecutorService service = Executors.newFixedThreadPool(5);
public static class Task implements Runnable {
private CountDownLatch latch;
public void setLatch(CountDownLatch latch) {
this.latch = latch;
}
@SneakyThrows
@Override
public void run() {
// 建立一个Socket连接发送数据
Socket socket = new Socket("127.0.0.1",10006);
// ...
// 执行最后调用如下方法
latch.countDown();
}
}
// 真实的代码这里的过程为,每次往线程池里面放一批任务,这一批任务执行完毕,再放下一批任务
// 即循环调用如下方法
@SneakyThrows
public void runTask(List<Task> taskList) {
CountDownLatch latch = new CountDownLatch(5);
taskList.forEach(item -> {
item.setLatch(latch);
service.submit(item);
});
latch.await();
}
}
LockSupport.park()メソッドでWAITING状態のスレッドがブロックされていることを思い出してください(上の図のJMCツールを使用)
エピソードを書くために、この小さなパートナーは、このコードが1年間オンラインで実行されており、問題がないことを常に強調してきました。なぜ彼は問題を抱えているので、彼の解決策は常に彼が変更した部分を調べることですが、彼は問題を見ることはありません。
また、一部のバグは特定のシナリオでのみ発生するため、私の考え方は彼とは異なります。前のコードに問題がないとは思わないでください。問題自体から始めてください。
Javaスレッドのステータス
問題を見つけたら、基本的な知識はまだ非常に重要です、それを確認してください
単純なスレッドの状態を次の図に示します
。Javaスレッドスレッド内に列挙型の内部クラスStateがあり、Java言語のスレッド状態の列挙値を定義します。
- NEW(初期化状態)
- RUNNABLE(操作可能/実行状態)
- BLOCKED(ブロック状態)
- WAITING(時間制限なしで待機)
- TIMED_WAITING(時間制限のある待機)
- TERMINATED(端末状態)
Javaは、オペレーティングシステムレベルでのブロッキング状態を、BLOCK、WAITING、およびTIMED_WAITINGの3つの状態に細分化します。
NEW:新しい状態、スレッドが作成されたが開始されていない状態。スレッドを作成する方法は3つあります
- Threadクラスを継承します
- Runnableインターフェースを実装する
- Callableインターフェースを実装する
私たちは最も一般的にこの方法でインターフェースを実装します。RunnableインターフェースとCallableインターフェースの違いは次のとおりです。
- Runnableは戻り値を取得できませんが、Callableは戻り値を取得できます
- Runnableは例外をスローできませんが、Callableは例外をスローできます
RUNNABLE(準備完了状態):start
RUNNING(実行状態)を呼び出した後の実行前の状態:スレッドは実行中
BLOCKED(ブロック状態):次の状態になります。次の状況があります。
- BLOCK(同期ブロッキング):ロックは、同期メソッドまたはコードブロックへの入力を待機するなど、他のスレッドによって占有されます
- WAITING(アクティブブロッキング):Object.wait()、Thread.join()などを実行します。
- TIMED_WAITING(ブロッキングを待機中):Object.wait(long)、Thread.sleep(long)などを実行します。
DEAD(終了状態):スレッドの実行が完了し、
最後にさまざまなメソッドがスレッド状態図に追加されます
シーンの復元
スレッドWAITINGは、一般に次の3つの方法のいずれかと呼ばれます。
- Object.wait()
- Thread.join()
- LockSupport.park()
トラブルシューティングプロセスは次のとおりです
-
Object.wait()とThread.join()がコードで呼び出されていないことを明確にした後、java.util.utilが原因で、java.util.concurrentパッケージのツールクラスによって引き起こされたスレッドブロッキングが呼び出されたと基本的に判断されます。 .concurrentパッケージ以下のツールは頻繁にLockSupport.park()を使用します
-
次に、CountDownLatchを使用することで問題が発生し、他のスレッドが終了し、1つのスレッドのみが実行され、他のスレッドがブロックされて待機していると判断できます。
-
では、このRUNNABLEスレッドは何をしたのでしょうか、なぜ終了しなかったのでしょうか。このとき、記事の冒頭の写真が方向性を示しており、このスレッドはネットワーク読み取りでブロックされていました。
-
ネットワーク読み取りでスタックしているため、接続タイムアウト時間、または読み取りのタイムアウト時間を設定していない必要があります。私が尋ねたとき、それは私が思ったのと同じで、設定はありませんでした
セットアップ後、彼はローカルで実行しましたが、最初は正常に実行されていましたが、直接例外を
スローしましたSocketTimeoutException:接続がタイムアウトしました(接続がタイムアウトしました)
SocketException:接続がリセットされました(サーバーは接続を閉じましたが、クライアントはまだ読み取り中です接続からのデータ)
では、なぜプログラムは最初は正常に実行されたのでしょうか。この接続異常は後で報告しましたか?
- サーバーは確かに並行しすぎています
- サーバーのネットワーク要求はBIOによって実現されます。1つの要求で1つのスレッドが作成され、高い同時実行性をサポートできません。
理由は?サーバーが実際にBIOを使用して実装されていることを確認するために、サーバーの開発者を見つけるように友人に依頼しました。Nettyはネットワークリクエストには使用されませんが、それでもあなたの気まぐれです!
私のフォローアップNettyの記事を楽しみにして、この種のことは二度と起こらないはずです。