blog

Javaマルチスレッド学習ノート - 新しいスレッドを作成する4つの方法

Java言語には、マルチスレッドのサポートが組み込まれています。Javaプログラムを起動すると、実際には、JVMプロセスの開始であり、その後、JVMは、メインメソッドを実行するメインスレッドを開始し、...

May 14, 2020 · 4 min. read
シェア

Java言語にはマルチスレッド・サポートが組み込まれています。Javaプログラムを開始すると、JVMプロセスが実際に開始され、次にJVMがメイン・スレッドを開始してmain()メソッドを実行します。main()メソッドでは、他のスレッドを作成して開始することができます。

基本的に、Javaで新しいスレッドを作成するのは、Threadインスタンスをインスタンス化するのと同じくらい簡単です。

Thread t = new Thread();

しかし、スレッドを開始するためにインスタンスの start() メソッドが呼び出されると、スレッドは実際には何もせずに終了してしまいます。なぜなら、Runnable インターフェースをオーバーライドする Thread クラスの run() メソッドのメソッド本体は以下のようになっているからです:

@Override
public void run() {
	if (target != null) {
		target.run();
	}
}

Threadコンストラクタが引数を渡さない場合、ターゲットはNULLとなり、スレッドは当然何も実行しません。

Javaでは、指定したコードを実行できる新しいスレッドを作成する方法がいくつかあります:

  1. Thread クラスからカスタム・クラスを派生させ、run() メソッドをオーバーライドします。
  2. Thread インスタンスを作成するときは、Runnadble インターフェースのインスタンスを渡し、インター フェースで定義された run() 抽象メソッドを実装します。
  3. Callable インターフェイスを実装し、call() メソッドをオーバーライドし、それを FutureTask クラスのオブジェクトにラップします。
  4. スレッドプールを使って新しいスレッドを作成します。
  1. メソッド 1 と 2 も、戻り値のない run() メソッドをオーバーライドして新しいスレッドを作成するため、戻り値を取得できません。
  2. 実際のプロジェクト・エンジニアリングでは、アプリケーション自体で明示的にスレッドを作成しない方がよく、スレッド・リソースはスレッド・プールを通じて提供されます。スレッドプールを使用する利点は、スレッドの作成と破棄にかかる時間とシステムリソースのオーバーヘッドを削減し、リソース不足の問題を解決することです。スレッドプールを使用しない場合、同じ種類のスレッドを大量に作成してメモリ不足に陥ったり、「オーバースイッチ」したりする可能性があります。

Threadクラスを継承し、run()メソッドをオーバーライドします。

public class ExecuteThread {
 public static void main(String[] args) {
 Thread t = new MyThread();
 t.start();	// 新しいスレッドを開始する
 }
}
// スレッド "から継承したカスタム・クラス
class MyThread extends Thread {
 @Override
 public void run() {
 System.out.println("start new thread!");
 }
}

また、その型のスレッドを一度だけ使用する必要がある場合は、匿名内部クラスを使用することで、コードをよりシンプルにすることができます:

public class ExecuteThread {
 public static void main(String[] args) {
 Thread t = new Thread() {
 // 匿名内部クラスによる run() メソッドのオーバーライド
 	@Override
 		public void run() {
 		System.out.println("start new thread!");
 		}
 };
 t.start();	// 新しいスレッドを開始する
 }
}
  • 長所:シンプルなコーディング、わかりやすい。
  • 欠点:他のクラスを継承できない、単一関数。

Runnadbleインターフェイスの実装、run()抽象メソッドの実装

public class ExecuteThread {
 public static void main(String[] args) {
 Thread t = new Thread(new MyRunnable());
 t.start(); // 新しいスレッドを開始する
 }
}
class MyRunnable implements Runnable {
 @Override
 public void run() {
 System.out.println("start new thread!");
 }
}

あるいは、Java 8で導入されたラムダ構文でさらに省略することもできます:

public class ExecuteThread {
 public static void main(String[] args) {
 Thread t = new Thread(() -> {
 System.out.println("start new thread!");
 });
 t.start(); // 新しいスレッドを開始する
 }
}
  • 長所
    1. 受信インスタンスはRunnadbleインターフェースを実装しているため、他のクラスを継承することができ、単一継承の制限を避けることができます。
    2. 同じプログラムコードの複数のスレッドがリソースを共有するのに適しており、分離された操作、コードとスレッドの独立性を実現します。
  • 短所:実装が比較的複雑。

Callable インターフェイスを実装し、call() メソッドを書き換えて、Thread コンストラクタに渡される FutureTask オブジェクトにパッケージ化します。

public class ExecuteThread {
 public static void main(String[] args) throws ExecutionException, InterruptedException {
 Callable<String> callableInstance = new MyCallable<>("return value");
 // Callable インタフェース実装のインスタンスの call() メソッドの返り値をカプセル化する Callable インタフェースのインスタンスをラップするには、FutureTask クラスを使用する。
 FutureTask<String> task = new FutureTask<>(callableInstance);
 Thread t = new Thread(task);
 t.start(); // 新しいスレッドを開始する
 // FutureTaskインスタンスのget()メソッドを呼び出して、新しいスレッドの実行終了戻り値を取得する。
 System.out.println(task.get());
 }
}
class MyCallable<V> implements Callable<V> {
 private V toReturn;
 public MyCallable (V val) {
 toReturn = val;
 }
 @Override
 public V call() throws Exception{
 System.out.println("start new thread!");
 return toReturn;
 }
}
  • 利点:メソッド2とは対照的に、戻り値を得ることができます。
  • 短所:導入が複雑。

スレッドプールを使った新しいスレッドの作成

スレッドプールの使用は、システムのオーバーヘッドが大きいため、スレッドの頻繁な作成と破棄を避けるために、リソースの再利用を達成するために 。このノートの特定の使用:。

Read next

Javaラムダ式がよくわかる

1.匿名の内部クラスの実装 匿名の内部クラスはまだクラスですが、ちょうどプログラマが指定されたクラス名を表示する必要はありません、コンパイラは自動的にクラスの名前を取ります。したがって、コードの次の形式を持っている場合は、コンパイル後に2つのクラスファイルが生成されます:2ラムダ式の実装ラムダ式を達成するための命令を介して、ラムダ式を書くことはありません...

May 13, 2020 · 9 min read