JVMでのスレッドの動作

スレッド化とは、アプリケーションのパフォーマンスを向上させるためにプログラミングプロセスを同時に実行することを指します。ビジネスアプリケーションでスレッドを直接操作することはそれほど一般的ではありませんが、Javaフレームワークでは常に使用されています。

例として、Spring Batchのように大量の情報を処理するフレームワークは、スレッドを使用してデータを管理します。スレッドまたはCPUプロセスを同時に操作すると、パフォーマンスが向上し、プログラムがより高速で効率的になります。

ソースコードを入手する

このJavaチャレンジャーのコードを入手してください。例に従いながら、独自のテストを実行できます。

最初のスレッドを見つける:Javaのmain()メソッド

Javaスレッドを直接操作したことがない場合でも、Javaのmain()メソッドにはメインスレッドが含まれているため、間接的に操作したことがあります。main()メソッドを実行するたびに、mainも実行しましたThread

Threadクラスを学習することは、Javaプログラムでスレッドがどのように機能するかを理解するのに非常に役立ちます。次にcurrentThread().getName()示すように、メソッドを呼び出すことで実行中のスレッドにアクセスできます。

 public class MainThread { public static void main(String... mainThread) { System.out.println(Thread.currentThread().getName()); } } 

このコードは、現在実行されているスレッドを識別する「メイン」を出力します。実行されているスレッドを識別する方法を知ることは、スレッドの概念を吸収するための最初のステップです。

Javaスレッドのライフサイクル

スレッドを操作するときは、スレッドの状態に注意することが重要です。Javaスレッドのライフサイクルは、次の6つのスレッド状態で構成されます。

  • 新規:新規Thread()がインスタンス化されました。
  • 実行可能Threadstart()メソッドが呼び出されました。
  • 実行中start()メソッドが呼び出され、スレッドが実行されています。
  • 中断:スレッドを一時的に中断され、そして別のスレッドによって再開することができます。
  • ブロック済み:スレッドは実行の機会を待っています。これは、1つのスレッドがすでにsynchronized()メソッドを呼び出しており、次のスレッドが終了するまで待機する必要がある場合に発生します。
  • 終了:スレッドの実行が完了しました。
ラファエル・チネラート・デル・ネロ

スレッドの状態について調べて理解することはまだまだありますが、このJavaの課題を解決するには、図1の情報で十分です。

並行処理:スレッドクラスの拡張

最も単純な並行処理はThread、以下に示すように、クラスを拡張することによって実行されます。

 public class InheritingThread extends Thread { InheritingThread(String threadName) { super(threadName); } public static void main(String... inheriting) { System.out.println(Thread.currentThread().getName() + " is running"); new InheritingThread("inheritingThread").start(); } @Override public void run() { System.out.println(Thread.currentThread().getName() + " is running"); } } 

ここでは、MainThreadとの2つのスレッドを実行していますInheritingThreadstart()newを使用してメソッドを呼び出すと、メソッドinheritingThread()内のロジックrun()が実行されます。

また、Threadクラスコンストラクターで2番目のスレッドの名前を渡すため、出力は次のようになります。

 main is running. inheritingThread is running. 

実行可能なインターフェイス

継承を使用する代わりに、Runnableインターフェイスを実装できます。コンストラクターRunnable内で渡すThreadと、結合が少なくなり、柔軟性が高まります。を渡した後、前の例で行ったのとまったく同じようにメソッドRunnableを呼び出すことができstart()ます。

 public class RunnableThread implements Runnable { public static void main(String... runnableThread) { System.out.println(Thread.currentThread().getName()); new Thread(new RunnableThread()).start(); } @Override public void run() { System.out.println(Thread.currentThread().getName()); } } 

非デーモンスレッドとデーモンスレッド

実行に関しては、2つのタイプのスレッドがあります。

  • 非デーモンスレッドは最後まで実行されます。メインスレッドは、デーモン以外のスレッドの良い例です。強制的にプログラムを完了させmain()ない限り、コードインは常に最後まで実行されSystem.exit()ます。
  • デーモンスレッドは反対側の端まで実行する必要がない基本的なプロセスです。

ルールを覚えておいてください:囲んでいる非デーモンスレッドがデーモンスレッドの前に終了した場合、デーモンスレッドは終了するまで実行されません。

デーモンスレッドと非デーモンスレッドの関係をよりよく理解するには、次の例を調べてください。

 import java.util.stream.IntStream; public class NonDaemonAndDaemonThread { public static void main(String... nonDaemonAndDaemon) throws InterruptedException { System.out.println("Starting the execution in the Thread " + Thread.currentThread().getName()); Thread daemonThread = new Thread(() -> IntStream.rangeClosed(1, 100000) .forEach(System.out::println)); daemonThread.setDaemon(true); daemonThread.start(); Thread.sleep(10); System.out.println("End of the execution in the Thread " + Thread.currentThread().getName()); } } 

この例では、デーモンスレッドを使用して、1〜100,000の範囲を宣言し、それらすべてを繰り返してから、出力しました。ただし、デーモン以外のメインスレッドが最初に終了した場合、デーモンスレッドは実行を完了しないことに注意してください。

出力は次のように進行します。

  1. メインスレッドでの実行の開始。
  2. 1からおそらく100,000までの数字を印刷します。
  3. メインスレッドでの実行の終了。おそらく100,000への反復が完了する前です。

最終的な出力は、JVMの実装によって異なります。

そして、それは私の次のポイントに私をもたらします:スレッドは予測不可能です。

スレッドの優先順位とJVM

setPriorityメソッドを使用してスレッドの実行に優先順位を付けることは可能ですが、その処理方法はJVMの実装によって異なります。Linux、MacOS、およびWindowsはすべて異なるJVM実装を持っており、それぞれが独自のデフォルトに従ってスレッドの優先順位を処理します。

ただし、設定したスレッドの優先度は、スレッド呼び出しの順序に影響します。Threadクラスで宣言されている3つの定数は次のとおりです。

 /** * The minimum priority that a thread can have. */ public static final int MIN_PRIORITY = 1; /** * The default priority that is assigned to a thread. */ public static final int NORM_PRIORITY = 5; /** * The maximum priority that a thread can have. */ public static final int MAX_PRIORITY = 10; 

次のコードでいくつかのテストを実行して、最終的にどの実行優先度になるかを確認してください。

 public class ThreadPriority { public static void main(String... threadPriority) { Thread moeThread = new Thread(() -> System.out.println("Moe")); Thread barneyThread = new Thread(() -> System.out.println("Barney")); Thread homerThread = new Thread(() -> System.out.println("Homer")); moeThread.setPriority(Thread.MAX_PRIORITY); barneyThread.setPriority(Thread.NORM_PRIORITY); homerThread.setPriority(Thread.MIN_PRIORITY); homerThread.start(); barneyThread.start(); moeThread.start(); } } 

moeThreadとして設定してもMAX_PRIORITY、このスレッドが最初に実行されることは期待できません。代わりに、実行の順序はランダムになります。

定数と列挙型

このThreadクラスはJava1.0で導入されました。当時、優先順位は列挙型ではなく定数を使用して設定されていました。ただし、定数の使用には問題があります。1から10の範囲外の優先順位番号を渡すと、setPriority()メソッドはIllegalArgumentExceptionをスローします。現在、列挙型を使用してこの問題を回避できます。列挙型を使用すると、不正な引数を渡すことができなくなります。これにより、コードが簡素化され、実行をより細かく制御できるようになります。

Javaスレッドに挑戦してください!

スレッドについて少し学びましたが、この投稿のJavaチャレンジには十分です。

まず、次のコードを調べます。

 public class ThreadChallenge { private static int wolverineAdrenaline = 10; public static void main(String... doYourBest) { new Motorcycle("Harley Davidson").start(); Motorcycle fastBike = new Motorcycle("Dodge Tomahawk"); fastBike.setPriority(Thread.MAX_PRIORITY); fastBike.setDaemon(false); fastBike.start(); Motorcycle yamaha = new Motorcycle("Yamaha YZF"); yamaha.setPriority(Thread.MIN_PRIORITY); yamaha.start(); } static class Motorcycle extends Thread { Motorcycle(String bikeName) { super(bikeName); } @Override public void run() { wolverineAdrenaline++; if (wolverineAdrenaline == 13) { System.out.println(this.getName()); } } } } 

このコードの出力はどうなりますか?コードを分析し、学んだことに基づいて、自分で答えを判断してみてください。

A.ハーレーダビッドソン

B.ダッジトマホーク

C.ヤマハYZF

D.不確定

今何があったの?スレッドの動作を理解する

上記のコードでは、3つのスレッドを作成しました。最初のスレッドはでHarley Davidson、このスレッドにデフォルトの優先度を割り当てました。2番目のスレッドにはDodge Tomahawk、が割り当てられMAX_PRIORITYます。3つ目はYamaha YZFMIN_PRIORITYです。次に、スレッドを開始しました。

In order to determine the order the threads will run in, you might first note that the Motorcycle class extends the Thread class, and that we've passed the thread name in the constructor. We've also overridden the run() method with a condition: if wolverineAdrenaline is equals to 13.

Even though Yamaha YZF is the third thread in our order of execution, and has MIN_PRIORITY, there's no guarantee that it will be executed last for all JVM implementations.

You might also note that in this example we set the Dodge Tomahawk thread as daemon. Because it's a daemon thread, Dodge Tomahawk may never complete execution. But the other two threads are non-daemon by default, so the Harley Davidson and Yamaha YZF threads will definitely complete their execution.

To conclude, the result will be D: Indeterminate, because there is no guarantee that the thread scheduler will follow our order of execution or thread priority.

Remember, we can't rely on program logic (order of threads or thread priority) to predict the JVM's order of execution.

Video challenge! Debugging variable arguments

Debugging is one of the easiest ways to fully absorb programming concepts while also improving your code. In this video you can follow along while I debug and explain the thread behavior challenge:

Common mistakes with Java threads

  • Invoking the run() method to try to start a new thread.
  • Trying to start a thread twice (this will cause an IllegalThreadStateException).
  • Allowing multiple processes to change the state of an object when it shouldn't change.
  • Writing program logic that relies on thread priority (you can't predict it).
  • Relying on the order of thread execution--even if we start a thread first, there is no guarantee it will be executed first.

What to remember about Java threads

  • Invoke the start() method to start a Thread.
  • It's possible to extend the Thread class directly in order to use threads.
  • It's possible to implement a thread action inside a Runnable interface.
  • Thread priority depends on the JVM implementation.
  • Thread behavior will always depend on the JVM implementation.
  • A daemon thread won't complete if an enclosing non-daemon thread ends first.

Learn more about Java threads on JavaWorld

  • Read the Java 101 threads series to learn more about threads and runnables, thread synchronization, thread scheduling with wait/notify, and thread death.
  • Modern threading: A Java concurrency primer introduces java.util.concurrent and answers common questions for developers new to Java concurrency.
  • Modern threading for not-quite-beginners offers more advanced tips and best practices for working with java.util.concurrent.

More from Rafael

  • Get more quick code tips: Read all the posts in the Java Challengers series.
  • Build your Java skills: Visit the Java Dev Gym for a code workout.
  • Want to work on stress free projects and write bug-free code? Visit the NoBugsProject for your copy of No Bugs, No Stress - Create a Life-Changing Software Without Destroying Your Life.

このストーリー「JVMでのスレッドの動作」は、もともとJavaWorldによって公開されました。