JavaRush /Java Blog /Random-JA /Java のマルチスレッド: 本質、利点、およびよくある落とし穴

Java のマルチスレッド: 本質、利点、およびよくある落とし穴

Random-JA グループに公開済み
こんにちは!まず第一に、おめでとうございます。Java のマルチスレッドのトピックに到達しました。これは重大な成果ですが、道のりは長いです。ただし、準備をしてください。これはコースの中で最も難しいトピックの 1 つです。そして重要なのは、ここで複雑なクラスや多くのメソッドが使用されているということではありません。むしろ、20 個もありません。それよりも、少し考え方を変える必要があります。以前は、プログラムは順番に実行されていました。コードの一部の行は他の行の後に続き、一部のメソッドは他の行の後に続き、全体的にはすべてが明確でした。まず何かを計算し、その結果をコンソールに表示して、プログラムを終了します。マルチスレッドを理解するには、同時実行性の観点から考えるのが最善です。非常に単純なことから始めましょう :)Java のマルチスレッド: 本質、利点、およびよくある落とし穴 - 1あなたの家族がある家から別の家に引っ越していると想像してください。引っ越しの重要な部分は本の梱包です。たくさんの本が溜まってしまったので、箱に入れる必要があります。今、あなただけが自由です。お母さんは食べ物の準備をし、兄は服を集め、妹は店に行きました。少なくとも一人で管理することは可能であり、遅かれ早かれそのタスクを自分で完了することもできますが、それには多くの時間がかかります。しかし、20分後には妹が店から戻ってくるので、他にやることはありません。それで彼女はあなたに加わることができます。タスクは変わらず、本を箱に詰めるというものでした。単純に2倍の速度で実行されます。なぜ?作業は並行して行われるためです。2 つの異なる「スレッド」(あなたとあなたの妹) が同じタスクを同時に実行しているため、何も変わらない場合、すべてを 1 人で行う場合と比べて、時間の差は非常に大きくなります。あなたの兄弟がすぐに自分の仕事を完了すれば、彼はあなたを助けることができ、物事はさらに早く進むでしょう。

Java でマルチスレッドによって解決される問題

基本的に、Java マルチスレッドは、次の 2 つの主要な問題を解決するために発明されました。
  1. 複数のアクションを同時に実行します。

    上の例では、異なるスレッド (つまり、家族) が、皿を洗う、店に行く、物を折りたたむなど、いくつかのアクションを並行して実行しました。

    より「プログラマー」の例を挙げることができます。ユーザー インターフェイスを備えたプログラムがあると想像してください。[続行] ボタンをクリックすると、プログラム内でいくつかの計算が行われ、ユーザーには次のインターフェイス画面が表示されます。これらのアクションを順番に実行すると、「続行」ボタンをクリックした後、プログラムは単にフリーズします。すべての内部計算が完了し、プログラムがインターフェースの描画を開始する部分に到達するまで、ユーザーには「続行」ボタンのある同じ画面が表示されます。

    さて、数分待ちましょう!

    Java のマルチスレッド: 本質、利点、およびよくある落とし穴 - 3

    プログラムを作り直すこともできますし、プログラマーが言うように「並列化」することもできます。必要な計算を 1 つのスレッドで実行し、インターフェイスのレンダリングを別のスレッドで実行します。ほとんどのコンピュータには、これに必要なリソースが十分にあります。この場合、プログラムは「愚か」ではなく、ユーザーは内部で何が起こっているかを心配することなく、静かにインターフェイス画面間を移動します。干渉しません:)

  2. 計算を高速化します。

    ここではすべてがはるかに簡単です。プロセッサーに複数のコアがあり、ほとんどのプロセッサーがマルチコアになっている場合、タスクのリストは複数のコアで並行して解決できます。明らかに、1,000 の問題を解決する必要があり、それぞれが 1 秒で解決される場合、1 つのコアでは 1,000 秒、2 つのコアでは 500 秒、3 つのコアでは 333 秒強というようにリストに対処します。

しかし、すでに講義で読んだとおり、現代のシステムは非常にスマートであり、1 つのコンピューティング コア上でも、タスクが交互に実行される場合の並列処理、または擬似並列処理を実装できます。一般的なものから具体的なものに移り、マルチスレッドに関連する Java ライブラリのメイン クラスである java.lang.Thread について理解しましょう。厳密に言えば、Java のスレッドは クラス のインスタンスによって表されますThread。つまり、10 個のスレッドを作成して実行するには、このクラスの 10 個のオブジェクトが必要になります。最も単純な例を書いてみましょう。
public class MyFirstThread extends Thread {

   @Override
   public void run() {
       System.out.println("I'm Thread! My name is " + getName());
   }
}
スレッドを作成して起動するには、クラスを作成し、それを から継承する必要がありますjava.langThreadそしてその中のメソッドをオーバーライドしますrun()。最後のものが非常に重要です。run()スレッドが実行する必要があるロジックを規定するのはメソッド内です。ここで、インスタンスを作成してMyFirstThread実行すると、メソッドはrun()その名前を含む行をコンソールに出力します。メソッドはgetName()、自動的に割り当てられるスレッドの「システム」名を出力します。しかし、実際のところ、なぜ「if」なのでしょうか?作成してテストしてみましょう!
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 です。MyFirstThreadを継承する 10 個のスレッド (オブジェクト) を作成しThread、オブジェクトのメソッドを呼び出して起動しますstart()。メソッドを呼び出すと、start()そのメソッドが動作し始めrun()、そのメソッド内に記述されたロジックが実行されます。スレッド名の順序が異なっていることに注意してください。非常に奇妙ですが、なぜ順番に実行されなかったのでしょう: Thread-0Thread-1などThread-2。これはまさに、標準的な「逐次的」思考が機能しない例です。実際、この場合、10 個のスレッドを作成して起動するコマンドのみを発行します。どの順序で起動するかは、オペレーティング システム内の特別なメカニズムであるスレッド スケジューラによって決定されます。それがどのように正確に構造化され、どのような原則に基づいて意思決定が行われるかは非常に複雑なトピックなので、ここでは立ち入りません。覚えておくべき主な点は、プログラマはスレッドの実行順序を制御できないということです。状況の深刻さを理解するには、main()上記の例のメソッドをあと数回実行してみてください。2 番目のコンソール出力: I'm Thread! 私の名前は 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 つの特有の問題、デッドロックと競合状態があります。デッドロックとは、複数のスレッドが相互に占有されているリソースを待機しており、どのスレッドも実行を続行できない状況です。これについては今後の講義で詳しく説明しますが、今のところはこの例で十分です。 Java のマルチスレッド: 本質、利点、およびよくある落とし穴 - 4 スレッド 1 がオブジェクト 1 を操作し、スレッド 2 がオブジェクト 2 を操作していると想像してください。プログラムは次のように書かれます。
  1. スレッド 2 がオブジェクト 2 での動作を停止してオブジェクト 1 に切り替わるとすぐに、スレッド 1 はオブジェクト 1 での動作を停止し、オブジェクト 2 に切り替わります。
  2. スレッド 1 がオブジェクト 1 での動作を停止してオブジェクト 2 に切り替わるとすぐに、スレッド 2 はオブジェクト 2 での動作を停止し、オブジェクト 1 に切り替わります。
マルチスレッドに関する深い知識がなくても、それを実行しても何も起こらないことは簡単に理解できます。スレッドは場所を変えることはなく、永遠にお互いを待ち続けます。この間違いは明白であるように見えますが、実際はそうではありません。プログラムへの組み込みを簡単に許可できます。次の講義では、デッドロックを引き起こすコードの例を見ていきます。ところで、Quora には、デッドロックとは何かを説明する優れた実際の例があります。「インドの一部の州では、農民として登録しない限り農地を販売してくれません。ただし、農地を所有していないと農業者として登録されません。」素晴らしい、何と言えばいいでしょうか!:) さて、レースコンディション、つまりレースの状態についてです。 競合状態は、マルチスレッド システムまたはアプリケーションの設計上の欠陥であり、システムまたはアプリケーションの動作がコード部分の実行順序に依存します。実行中のスレッドの例を思い出してください。 Java のマルチスレッド: 本質、長所、およびよくある落とし穴 - 5
public class MyFirstThread extends Thread {

   @Override
   public void run() {
       System.out.println("Выполнен поток " + 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 はストーブをオンにします。Stream-2はフライパンを取り出してコンロの上に置きます。ストリーム 3 はストーブに火をつけます。ストリーム 4 は油をパンに注ぎます。ストリーム5は卵を割り、フライパンに注ぎます。ストリーム 6 は砲弾をゴミ箱に捨てます。Stream-7 は完成したスクランブルエッグを火から下ろします。Potok-8 はお皿にスクランブルエッグを置きます。ストリーム9は皿を洗います。 プログラムの結果を見てください。 Thread-0 が実行されました Thread-2 スレッドが実行されました Thread-1 スレッドが実行されました Thread-4 スレッドが実行されました Thread-9 スレッドが実行されました Thread-5 スレッドが実行されました Thread-8 スレッドが実行されました Thread-7 スレッドが実行されました Thread-7実行されたスレッド -3 スレッド -6 実行されたスレッド スクリプトは楽しいですか? :) そしてすべては、プログラムの動作がスレッドの実行順序に依存しているためです。順序を少しでも破ると、キッチンは地獄に変わり、狂ったロボットが周囲のあらゆるものを破壊します。これはマルチスレッド プログラミングの一般的な問題でもあり、何度も耳にするでしょう。講義の最後に、マルチスレッドに関する本をお勧めします。
Java のマルチスレッド: 本質、利点、およびよくある落とし穴 - 6
『Java Concurrency in Practice』は 2006 年に書かれたものですが、その関連性は失われていません。Java でのマルチスレッド プログラミングについて説明しており、基本から始まり、最も一般的なエラーとアンチパターンのリストで終わります。マルチスレッド プログラミングの第一人者になろうと決心したなら、この本は必読です。次回の講義でお会いしましょう!:)
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION