はじめに (対象読者・この記事でわかること)
この記事は、Javaでのマルチスレッドプログラミングに興味がある方、特にThreadクラスの使い方を学びたいプログラミング初学者から中級者の方を対象としています。
この記事を読むことで、Threadのstart()メソッドとrun()メソッドの重要な違いが理解でき、適切なマルチスレッドプログラミングが行えるようになります。また、実際のコード例を通じて、並行処理がもたらすメリットと注意点を学ぶことができます。
前提知識
この記事を読み進める上で、以下の知識があるとスムーズです。 - Javaの基本的な文法(クラス、メソッドの概念) - プログラミングの基礎知識
Threadクラスの基本:なぜマルチスレッドが必要なのか
Javaにおいて、Thread(スレッド)はプログラム内で独立して実行される処理の単位です。単一のプログラム内で複数の処理を同時に実行することで、ユーザーインターフェースの応答性を向上させたり、時間のかかる処理をバックグラウンドで実行したりできます。
例えば、ファイルのダウンロード中にプログレスバーを表示しながら、キャンセルボタンが押せるようにするなど、並行処理は現代のソフトウェア開発で不可欠な技術です。
start()とrun()の違い:並行処理の本質を理解する
JavaでThreadを扱う際、多くの初学者が陥る落とし穴がstart()メソッドとrun()メソッドの使い分けです。見た目は似ていますが、内部的には大きな違いがあります。
基本的な使い方と違い
まず、それぞれのメソッドがどのように動作するかを見てみましょう。
Java// Threadの定義 class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + ": " + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } // メインクラス public class ThreadExample { public static void main(String[] args) { MyThread thread1 = new MyThread(); MyThread thread2 = new MyThread(); // start()メソッドを使用した場合 System.out.println("=== start()メソッドを使用 ==="); thread1.start(); thread2.start(); // run()メソッドを直接呼び出した場合 System.out.println("\n=== run()メソッドを直接呼び出す ==="); MyThread thread3 = new MyThread(); MyThread thread4 = new MyThread(); thread3.run(); thread4.run(); } }
実行結果の違い
start()メソッドを使用した場合、2つのスレッドが並行して実行され、出力が交互に表示されます。一方、run()メソッドを直接呼び出すと、通常のメソッド呼び出しとして実行され、順番に処理が行われます。
内部的な仕組みの違い
start()メソッドが呼ばれると、Javaは新しいスレッドを作成し、そのスレッド内でrun()メソッドを実行します。これにより、複数のスレッドが並行して動作できます。
一方、run()メソッドを直接呼び出すと、現在のスレッド(通常はメインスレッド)で実行されるため、マルチスレッドのメリットを得ることができません。
実践的な例:並行処理のメリット
実際のアプリケーションでこれらの違いがどのように影響するかを見てみましょう。
Javaimport java.time.LocalTime; class DataProcessor extends Thread { private String dataName; public DataProcessor(String dataName) { this.dataName = dataName; } @Override public void run() { System.out.println(LocalTime.now() + " - " + dataName + "の処理を開始"); // 時間のかかる処理をシミュレート try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(LocalTime.now() + " - " + dataName + "の処理が完了"); } } public class ConcurrentProcessingExample { public static void main(String[] args) { System.out.println("=== 逐次処理(run()使用)=== "); long startTime1 = System.currentTimeMillis(); DataProcessor processor1 = new DataProcessor("データ1"); DataProcessor processor2 = new DataProcessor("データ2"); DataProcessor processor3 = new DataProcessor("データ3"); processor1.run(); processor2.run(); processor3.run(); long endTime1 = System.currentTimeMillis(); System.out.println("逐次処理の合計時間: " + (endTime1 - startTime1) + "ms\n"); System.out.println("=== 並行処理(start()使用)=== "); long startTime2 = System.currentTimeMillis(); DataProcessor processor4 = new DataProcessor("データ4"); DataProcessor processor5 = new DataProcessor("データ5"); DataProcessor processor6 = new DataProcessor("データ6"); processor4.start(); processor5.start(); processor6.start(); // すべてのスレッドが終了するのを待つ try { processor4.join(); processor5.join(); processor6.join(); } catch (InterruptedException e) { e.printStackTrace(); } long endTime2 = System.currentTimeMillis(); System.out.println("並行処理の合計時間: " + (endTime2 - startTime2) + "ms"); } }
ハマった点やエラー解決
初学者がよく犯す間違いに、同じThreadオブジェクトのstart()メソッドを複数回呼び出すことがあります。
JavaThread thread = new MyThread(); thread.start(); thread.start(); // IllegalThreadStateExceptionが発生
一度start()されたThreadは、それ以上開始することはできません。再度実行したい場合は、新しいThreadオブジェクトを作成する必要があります。
解決策
正しい実装方法は以下の通りです:
Java// 間違った例 Thread thread1 = new MyThread(); thread1.start(); // thread1.start(); // エラー! // 正しい例 Thread thread2 = new MyThread(); thread2.start(); Thread thread3 = new MyThread(); thread3.start();
まとめ
本記事では、JavaのThreadクラスのstart()メソッドとrun()メソッドの重要な違いを解説しました。
start()メソッドは新しいスレッドを作成して並行処理を実現run()メソッドを直接呼び出すと通常のメソッド呼び出しとして実行- 適切な使い分けにより、効率的なマルチスレッドプログラミングが可能
この記事を通して、Javaでの並行処理の基礎を理解し、実際のアプリケーションで効果的に活用できるようになりました。
今後は、ExecutorServiceやFork/Joinフレームワークなど、より高度な並行処理の仕組みについても記事にする予定です。
参考資料
