はじめに
急遽、Javaでマルチスレッドプログラムを作れという職場からの
無茶苦茶な要望に答えるべく速攻で勉強したことをまとめておく。
※ Javaの経験日数はまだ3日程度なのでミスしてたらご指摘ください。
結論:とりあえずサクッと起動したい場合
サブスレッド起動までのパスが少ない&サブスレッドをインスタンスとして保持しないなら、ラムダ式を組み合わせて
new Thread( () -> {サブスレッドで実行したいメソッド or 処理} ).start()
でよさそう。便利っすねぇー。
※僕の中では、ラムダ式はローカルな無名クラスと理解しています。
実行環境
自前でEclipseのインストールとかは後回しにしてとりあえず、文法を確かめるためにpaiza.ioを使いました。
サブスレッドの実装方法
以下の2パターンでスレッドを実装できるみたい。
1. Rnnableインタフェースを使用する。
2. Threadクラスを継承する。
1. Runnableインタフェースを使う
JavaにはRunnableインタフェースなるものがあるらしく、run()をオーバーライドすることで、スレッド時に実行される処理が実装できるようだ。
使い方は以下の通り。
1. Runnableインタフェースを実装したクラスのインスタンスを生成
2. コンストラクタにRunnnableオブジェクトを渡して、Thread型のオブジェクトを生成
3. Threadクラスのstart()メソッドをコールしてサブスレッドを起動
この通りに従って書いたサンプルコードがRunnableSample.javaです。
import java.util.*;
public class Main {
private static class RunnableSample implements Runnable{
private String thread_no;
RunnableSample(String str){
this.thread_no = str;
}
public void run(){
System.out.println("RunnableThread Starting No."+thread_no);
}
}
public static void main(String[] args) throws Exception {
String str1 = "1";
String str2 = "2";
RunnableSample runner1 = new RunnableSample(str1); // 1.
RunnableSample runner2 = new RunnableSample(str2); // 1.
Thread thread1 = new Thread(runner1); // 2.
Thread thread2 = new Thread(runner2); // 2.
thread1.start(); // 3.
thread2.start(); // 3.
Thread.sleep(200);
System.out.println("MainThread End");
}
}
RunnableThread Starting No.1
RunnableThread Starting No.2
MainThread End
MainクラスのローカルクラスとしてRunnableSampleクラスを定義しています。
MainスレッドからRunnableSampleクラスのオブジェクトを生成し、2つのサブスレッドとして実行します。
RunnableThread Starting No.1
RunnableThread Starting No.2
は逆順で出力される可能性がある。200msのスリープを挟んでいるのでMainThread Endはたいてい最後に出力されるでしょう。
2. Threadクラスを継承する。
次にThreadクラスを継承してサブスレッドの処理を実装する方法について書く。
使い方は次の通り。コードはThreadSample.java。
- Threadクラスのrun()をオーバーライドしたクラスのインスタンスを生成
- Threadクラスのstart()メソッドをコールしてサブスレッドを起動
import java.util.*;
public class Main {
private static class ThreadSample extends Thread{
private String thread_no;
ThreadSample(String str){
this.thread_no = str;
}
public void run(){
System.out.println("RunnableThread Starting No."+thread_no);
}
}
public static void main(String[] args) throws Exception {
String str1 = "1";
String str2 = "2";
ThreadSample thread1 = new ThreadSample(str1); // 1.
ThreadSample thread2 = new ThreadSample(str2); // 1.
thread1.start(); // 2.
thread2.start(); // 2.
Thread.sleep(200);
System.out.println("MainThread End");
}
}
RunnableThread Starting No.2
RunnableThread Starting No.1
MainThread End
実行結果は同じになる(今回はThread1とThread2の出力の順番が逆転した)。
結局「Runnableインタフェースの実装」と「Threadクラスの継承」どちらがいいのか?
基本的にはRunnableインタフェースを使った方がメリットが多そう。
- Javaは多重継承を許していないため、Threadクラスを継承すると他のクラスが継承できなくなってしまう。Runnableインタフェースを実装したクラスであれば他のクラスを継承することができる。
- Threadクラスが抱えるオーバーヘッドをそのまま受け継がなくてすむ。
- Threadクラスを継承した方がコードはスッキリしそう。
Runnableインタフェースでもすっきりと書けたら完璧やん。ということでやってみる。
Runnableインタフェースをラムダ式で記述を簡単にする。
Java8のラムダ式を理解するを参考にラムダ式でコードを簡潔にしてみる。
import java.util.*;
public class Main {
static private void subthread_run(String thread_no){
System.out.println("RunnableThread Starting No."+thread_no);
}
public static void main(String[] args) throws Exception {
String str1 = "1";
String str2 = "2";
new Thread( () -> {subthread_run(str1);} ).start();
new Thread( () -> {subthread_run(str2);} ).start();
Thread.sleep(200);
System.out.println("MainThread End");
}
}
RunnableThread Starting No.2
RunnableThread Starting No.1
MainThread End
詳しい記述方法はJava8のラムダ式を理解するを参考にしていただくとするが、難解に簡単に説明すると、先のRunnableSampleクラスをsubthread_runメソッドをもつ無名クラスとし、Threadクラスのオブジェクトをnewしてstartしている。
ラムダ式を用いることで随分とプログラムがすっきりとし可読性もぐっと上がった。Threadをインスタンスとして管理する必要がないならこの書き方が良さそうだ。次はSynchronizedについてまとめたい。
参考文献
世界で戦うプログラミング力を鍛える150問 Chapter.16:スレッドとロック