やあ!まず第一に、おめでとうございます。Java でのマルチスレッドのトピックに到達しました。これは重大な成果です。あなたは長い道のりを歩んできました。ただし、覚悟を決めてください。これはコースの中で最も難しいトピックの 1 つです。ここで複雑なクラスや多くのメソッドを使用しているわけではありません。実際に使用するのは 20 未満です。むしろ考え方を少し変える必要があります。以前は、プログラムは順番に実行されていました。コードのいくつかの行は他の行の後に、いくつかのメソッドは他の行の後に続き、すべてが基本的に明確でした。まず何かを計算し、結果をコンソールに表示してプログラムを終了します。マルチスレッドを理解するには、並列処理の観点から考えるとよいでしょう。非常に単純なものから始めましょう: ) あなたの家族が家から家へと引っ越していくところを想像してみてください。すべての本を集めることが引っ越しの重要な部分になります。たくさんの本が溜まってしまったので、箱に入れる必要があります。現在、対応できるのはあなただけです。お母さんは食べ物の準備をし、兄は服を梱包し、妹は店に行きました。一人でもなんとかなる。遅かれ早かれ、このタスクは自分で完了することになりますが、かなりの時間がかかります。しかし、お姉さんは 20 分でお店から戻ってくるので、他にやることはありません。それで彼女はあなたに加わることができます。タスクは変わっていません。本を箱に入れることです。ただし、実行速度は 2 倍になります。なぜ?なぜなら、作業は並行して行われているからです。2 つの異なる「スレッド」(あなたとあなたの妹) が同じタスクを同時に実行しています。そして何も変わらなければ、そうなると、すべてを自分で行う場合と比べて、大幅な時間差が生じてしまいます。兄弟がすぐに仕事を終えれば、あなたを助けることができ、物事はさらに早く進むでしょう。
マルチスレッド化で解決できる問題
マルチスレッドは、実際には次の 2 つの重要な目的を達成するために発明されました。-
複数のことを同時に実行します。
上の例では、異なるスレッド (家族メンバー) がいくつかのアクション (皿を洗う、店に行く、荷物を詰める) を並行して実行しました。
プログラミングにもっと密接に関連した例を提供できます。ユーザー インターフェイスを備えたプログラムがあるとします。プログラムで [続行] をクリックすると、いくつかの計算が行われ、次の画面が表示されます。これらのアクションが連続して実行された場合、ユーザーが「続行」ボタンをクリックした後、プログラムはハングするだけです。プログラムがすべての内部計算を実行し、ユーザー インターフェイスが更新される部分に到達するまで、ユーザーには [続行] ボタンが表示された画面が表示されます。
そうですね、数分待ってみます!
あるいは、プログラムを作り直すか、プログラマーが言うように「並列化」することもできます。1 つのスレッドで計算を実行し、別のスレッドでユーザー インターフェイスを描画しましょう。ほとんどのコンピュータには、これを行うのに十分なリソースがあります。この方法を採用すると、プログラムはフリーズせず、ユーザーは内部で何が起こっているかを気にせずに画面間をスムーズに移動できます。一方が他方に干渉することはありません:)
-
計算をより迅速に実行します。
ここではすべてがはるかに簡単です。プロセッサーに複数のコアがあり、今日のほとんどのプロセッサーには複数のコアがある場合、複数のコアがタスクのリストを並行して処理できます。明らかに、1000 個のタスクを実行する必要があり、それぞれに 1 秒かかる場合、1 つのコアでは 1000 秒、2 つのコアでは 500 秒、3 つのコアでは 333 秒強などでリストを完了できます。
public class MyFirstThread extends Thread {
@Override
public void run() {
System.out.println("I'm Thread! My name is " + getName());
}
}
スレッドを作成して実行するには、クラスを作成し、java.langを継承させる必要があります。Threadクラスを作成し、そのrun()メソッドをオーバーライドします。最後の要件は非常に重要です。run()メソッド内で、スレッドが実行するロジックを定義します。ここで、 MyFirstThreadのインスタンスを作成して実行すると、run()メソッドは名前を含む行を表示します。getName()メソッドは、自動的に割り当てられるスレッドの「システム」名を表示します。しかし、なぜ暫定的に話しているのでしょうか?作成して調べてみましょう!
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyFirstThread thread = new MyFirstThread();
thread.start();
}
}
}
コンソール出力: 私はスレッドです! 私の名前は Thread-2、スレッドです! 私の名前は Thread-1、スレッドです! 私の名前は Thread-0、スレッドです! 私の名前はスレッド 3、スレッドです! 私の名前は Thread-6、スレッドです! 私の名前は Thread-7、スレッドです! 私の名前は Thread-4、スレッドです! 私の名前は Thread-5、スレッドです! 私の名前は Thread-9、スレッドです! 私の名前は Thread-8 です。 10 個のスレッド ( Threadを継承するMyFirstThreadオブジェクト) を作成し、各オブジェクトでstart()メソッドを呼び出して開始しましょう。start()メソッドを呼び出した後、 run()メソッドのロジックが実行されます。 注:スレッド名は順序どおりではありません。順番に並んでいないのは奇妙です。、Thread-1、Thread-2など? 偶然にも、これは「逐次的」思考が適合しない時代の一例です。問題は、10 個のスレッドを作成して実行するコマンドしか提供されていないことです。オペレーティング システムの特別なメカニズムであるスレッド スケジューラが、スレッドの実行順序を決定します。その正確な設計と意思決定戦略については、深い議論の対象となるため、ここでは立ち入りません。覚えておくべき主な点は、プログラマはスレッドの実行順序を制御できないということです。状況の深刻さを理解するには、上の例の main() メソッドをあと数回実行してみてください。2 回目の実行時のコンソール出力: 私はスレッドです!私の名前は Thread-0、スレッドです! 私の名前は Thread-4、スレッドです! 私の名前はスレッド 3、スレッドです! 私の名前は Thread-2、スレッドです! 私の名前は Thread-1、スレッドです! 私の名前は Thread-5、スレッドです! 私の名前は Thread-6、スレッドです! 私の名前は Thread-8、スレッドです! 私の名前は Thread-9、スレッドです! 私の名前は Thread-7 3 回目の実行からのコンソール出力: 私は Thread! 私の名前は Thread-0、スレッドです! 私の名前はスレッド 3、スレッドです! 私の名前は Thread-1、スレッドです! 私の名前は Thread-2、スレッドです! 私の名前は Thread-6、スレッドです! 私の名前は Thread-4、スレッドです! 私の名前は Thread-9、スレッドです! 私の名前は Thread-5、スレッドです! 私の名前は Thread-7、スレッドです! 私の名前はスレッド8です
マルチスレッドによって生じる問題
書籍の例では、マルチスレッドによって非常に重要なタスクが解決され、プログラムが高速化できることがわかりました。多くの場合、何倍も速くなります。しかし、マルチスレッド化は難しいテーマであると考えられています。実際、不適切に使用すると、問題を解決するのではなく、問題を引き起こすことになります。私が「問題を引き起こす」と言っているのは、抽象的な意味ではありません。マルチスレッドによって発生する可能性のある 2 つの特有の問題、デッドロックと競合状態があります。デッドロックとは、複数のスレッドが相互に保持しているリソースを待機しており、どのスレッドも実行を継続できない状況です。これについては後続のレッスンで詳しく説明します。今のところは次の例で十分です。 Thread-1 が何らかの Object-1 と対話し、Thread-2 が Object-2 と対話すると想像してください。さらに、プログラムは次のように書かれています。- スレッド 2 がオブジェクト 2 との対話を停止してオブジェクト 1 に切り替わるとすぐに、スレッド 1 はオブジェクト 1 との対話を停止し、オブジェクト 2 に切り替わります。
- Thread-1 が Object-1 との対話を停止して Object-2 に切り替わるとすぐに、Thread-2 は Object-2 との対話を停止し、Object-1 に切り替わります。
public class MyFirstThread extends Thread {
@Override
public void run() {
System.out.println("Thread executed: " + getName());
}
}
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyFirstThread thread = new MyFirstThread();
thread.start();
}
}
}
ここで、プログラムが食べ物を調理するロボットの実行を担当していると想像してください。 Thread-0 は冷蔵庫から卵を取り出します。スレッド 1 がストーブをつけます。スレッド 2 は鍋を手に入れてコンロの上に置きます。スレッド 3 はストーブに火をつけます。スレッド 4 はパンに油を注ぎます。スレッド 5 は卵を割り、鍋に注ぎます。スレッド 6 は卵の殻をゴミ箱に捨てます。スレッド 7 は調理済みの卵をバーナーから取り出します。Thread-8 は調理した卵を皿に置きます。スレッド 9 は皿を洗います。 プログラムの結果を見てください: 実行されたスレッド: Thread-0 実行されたスレッド: Thread-2 実行されたスレッド Thread-1 実行されたスレッド: Thread-4 実行されたスレッド: Thread-9 実行されたスレッド: Thread-5 実行されたスレッド: Thread-8 スレッド実行されたスレッド: スレッド-7 実行されたスレッド: スレッド-3 これはコメディのルーティンですか?:) そしてすべては、プログラムの動作がスレッドの実行順序に依存しているためです。必要な順序をほんの少しでも違反すると、キッチンは地獄と化し、狂気のロボットが周囲のあらゆるものを破壊します。これは、マルチスレッド プログラミングでもよくある問題です。それについては何度も聞くことになるでしょう。このレッスンの締めくくりとして、マルチスレッドに関する本をお勧めしたいと思います。 「Java Concurrency in Practice」は 2006 年に書かれましたが、その関連性は失われていません。基本から最も一般的な間違いやアンチパターンまで、マルチスレッド Java プログラミングに特化しています。いつかマルチスレッドの第一人者になろうと決心したなら、この本は必読です。 次のレッスンでお会いしましょう!:)
GO TO FULL VERSION