Javaには並列処理という機能があるのをご存じでしょうか?
並列処理は複数の処理を同時並行で実施することが可能な機能です。
この機能を使用することでプログラムのパフォーマンスを何倍にも跳ね上げることができます。
個人でJavaを勉強しているだけですとそこまで使用頻度は高くないですが、仕事で大規模な開発などをするとなったら必要になってくる機能です。
そんな並列処理について私が学んだことをこの記事ではまとめていきます。
並列処理の方法
ではまず基礎的な並列処理の方法について解説していきます。
Javaでの処理はスタックという領域にメソッド入れたり出したりすることで処理を実施しています。
この処理の流れをスレッドと呼び、並列処理は複数個のスレッドを同時並行に進めることで実現しています。
プログラムとしては下記のようになっています。
【使用例】
public class Parallel {
public static void main(String[] args) {
Thread pa = new ParallelThread();
pa.start();
System.out.println("メイン処理");
}
}
public class ParallelThread extends Thread{
@Override
public void run() {
System.out.println("サブ処理");
}
}
【実行結果】
メイン処理
サブ処理
上記が並列処理を実現している基礎的なプログラムです。
まずメインメソッドのあるParallelクラスでParallelThreadをインスタンス化します。
そしてその変数をstartメソッドで実行します。
これによりParallelThreadクラスの方の処理が開始されます。
ParallelThreadの処理が動作しているのと同時にParalleクラスの方の処理も引き続き動作しています。
ParallelThreadの処理は実施するのに少し時間がかかるため、先にParallelクラスの方の処理が進み「メイン処理」という結果が出力され、その後にParallelThreadが実行されるとrunメソッドが実施されるため「サブ処理」という結果が出力されます。
流れとしては下記のようになっています。
ただしこのコードは簡単なものを使用したため、時間は全然かかりませんでしたが、実際の業務等で使用するとなった場合時間がかかる処理が結構出てくると思います。
そうなるとサブ処理の方が先に終わるということも出てきます。
このように並列処理はどちらが先に処理が実施完了するかというのは分かりません。
スレッドプールについて
初歩的な並列処理について解説しましたが、先ほどの並列処理には問題点が存在します。
それがスタック領域の無駄使いというものです。
必要な同時並行の処理の分だけスレッドを作成する必要が出てくるというものです。
いくつものスレッドを次々に作成して並列処理を実現していった場合、最初の方に作成したスレッドは後から作成するスレッドが作成される前に終了する可能性も存在しています。
例えば20個のスレッドを作成しようとした場合、1個目に作成したスレッドは20個目のスレッドを作成しようとしているときにはすでに終了している可能性も存在しています。
そういった時に活用されるのがスレッドプールという機能です。
あらかじめ必要なスレッドを空の状態で作成しておき、必要になったら使うという方法です。
そして使い終わったらそのスレッドは解放し、また新しいスレッドとして使いまわすということです。
使用方法は下記のコードを見ていただいたら分かるかと思います。
【使用例】
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Parallel2 {
public static void main(String[] args) {
ExecutorService ex = Executors.newFixedThreadPool(5);
for (int a=0;a<10;a++) {
ex.submit(() -> {
System.out.println(Thread.currentThread().getId());
});
}
}
}
【実行結果】
15
16
17
18
16
18
17
19
15
16
上記はスレッドプールを使用して、スレッドに割り当てられたIDを出力していくというものです。
事前に作成したスレッドは5つで、実行結果には5つのスレッドIDが出力されています。
スレッドが終了してもまた新しいスレッドが必要になったら空いているスレッドが使用されて使いまわされていることが分かるかと思います。
これがスレッドプールの方法です。
遅延実行の方法
最後に遅延実行について解説していきたいと思います。
今までのスレッドではメインスレッドを先に終わらしたいとなった場合でも、終了順序をコントロールすることができませんでした。
そこでここでは遅延実行の方法について解説していきます。
【使用例】
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Parallel {
public static void main(String[] args) throws Exception{
ScheduledExecutorService ex=Executors.newSingleThreadScheduledExecutor();
ex.schedule(() -> {
System.out.println("遅延実行終了");
ex.shutdown();
},5,TimeUnit.SECONDS);
}
}
【実行結果】
遅延実行終了
上記のプログラムを実行すると5秒後に「遅延実行終了」という文言が出力されます。
ScheduledExecutorServiceは遅延実行等を実施する際に使用するインタフェースです。
そしてnewSingleThreadScheduledExecutorでスレッドを一つ作成し、遅延実行を実施するためのscheduleメソッドを使用します。
メソッドの引数は第1引数に遅延実行後にする処理、第2引数には遅延実行する時間、第3引数には時間の単位を指定します。
上記コードですと5秒間遅延実行したら「遅延実行終了」という文言が出力され、スレッドを閉じるという処理内容となっています。
このようにして処理を遅延させることができます。
まとめ
この記事ではJavaの並列処理について解説してきました。
- 並列処理の方法
- スレッドプールについて
- 遅延実行の方法
並列処理には色々な機能があり、使用するメソッドやクラスもたくさんあります。
クラス名もとても長いものが多いため、すぐに覚えて区別することは難しいと思いますので、根気強く勉強していくことをオススメします。
以上で今回の記事は終了となります。
コメント