【Java入門】マルチスレッド処理を行う方法(Runnable・Thread・Executor)

Javaでマルチスレッド処理を行う方法について記載します。

1. マルチスレッド処理を行う方法

Javaでマルチスレッド処理を行うには、次の方法があります。

おすすめは、Executor を使用する方法です。

Executorのみスレッド処理からの戻り値を取得することができます。

No クラス
インタフェース
スレッドの
戻り値
実装方法・特徴
1 Runnable なし Runnableインタフェースを実装する。
2 Thread なし Threadクラスを継承したサブクラスを作成する。
3 Executor あり ・Executorフレームワークのメソッドを使用する。
・スレッドプールを使用できる。
・遅延実行や定期実行する処理を簡単に実装できる。

2. Runnableインタフェースを使用したマルチスレッド処理

Runnableインタフェースを使用したスレッド処理では、次のように処理を実装します。

① Runnableインタフェースを実装したクラスを作成

実装したクラス内で、runメソッドをオーバーライドします。
runメソッド内に、別スレッドで実行したい処理を実装します。

② スレッドの呼び出し

① で実装した runメソッドを呼び出すには、①のクラスのインスタンスを作成しstartメソッドを呼び出します。

startメソッドを呼び出すことで、runメソッドが別スレッドで呼び出されます。
注意点として、runメソッドを直接呼び出すと、メインスレッドで実行されます。

実行例
Runnableインタフェースを実装したクラスを作成して、メインスレッドと別スレッドの合わせて2スレッドで処理を行います。

実行結果は、処理毎に変わりますが、マルチスレッド( 実行結果の main と run )で処理が行われていることが分かります。

3. Threadクラスを使用したマルチスレッド処理

Threadクラスを使用する場合は、Threadクラスを継承したサブクラスを作成し、runメソッドをオーバーライドすることで、マルチスレッド処理を行います。

実行例

処理内容は、Runnableインタフェースと同様で、メイン・サブスレッドで 1秒毎に出力を行うプログラムです。

こちらも実行結果は、処理毎に変わりますが、マルチスレッド( 実行結果の main と run )で処理が行われていることが分かります。

4 . Executorインタフェースを使用したマルチスレッド処理

Executorインタフェースは、RunnableインタフェースやThreadクラスとは異なり、次のメリットがあります。

・スレッドの処理結果( 戻り値 )が取得できる。

・スレッドプールを使用して固定数のスレッドを容易に扱える。

・遅延実行や定期実行が容易に行える。

Executorを使用する場合は、それぞれの処理で使用するインスタンスをファクトリメソッド( インスタンスを生成するメソッド )で取得します。

ファクトリメソッドと特徴

No メソッド 特徴
1 newSingleThreadExecutor() シングルスレッドでタスクの処理を行います。
2 newFixedThreadPool(int スレッド数) 指定した数のスレッドでタスクの並列処理を行います。
3 newSingleThreadScheduledExecutor() シングルスレッドで遅延実行や定期的なタスクの処理を行います。

また、スレッドを実行した結果を返す/返さないは、タスクを実行する際のメソッドにより変わります。

タスク実行メソッドと戻り値

No メソッド 戻り値 特徴
1 execute( Runnable ) なし Runnableタスクを実行して戻り値を返しません。
2 submit( Runnable ) あり
( null )
Runnableタスクを実行して戻り値を返します。
3 submit( Callable ) あり Callableタスクを実行して戻り値を返します。

4-1. シングルスレッドでタスクの処理を行う

newSingleThreadExecutor メソッドを使用します。

構文

Executors.newSingleThreadExecutor()

戻り値

ExecutorService

実行例( 戻り値なし:executeメソッドでタスクを実行 )

実行結果からシングルスレッド( pool-1-thread-1 )で3回タスクが実行されていることが分かります。

次の実行例では、タスクの実行結果( 戻り値 )を取得します。

実行例( 戻り値あり:submitメソッドでタスクを実行 )

実行結果から、タスクから戻り値を取得できていることが分かります。

4-2. 指定した数のスレッドでタスクの並列処理を行う

newFixedThreadPool メソッドを使用します。

構文

Executors.newFixedThreadPool( int スレッド数 )

戻り値

ExecutorService

実行例( 戻り値なし:executeメソッドでタスクを実行 )

実行結果から、最初は pool-1-thread-1 と pool-1-thread-2 が同時にタスクを実行していることが分かります。

3タスク中の2タスクは、スレッドプールのthread-1、2 が使われて並列に実行されています。
thread-2 のタスクが先に終了しているので、3タスク目は thread-2 で実行されています。

次の実行例では、タスクの実行結果( 戻り値 )を取得します。

実行例( 戻り値あり:submitメソッドでタスクを実行 )

実行結果から、pool-1-thread-1、2 が使用されているため固定スレッド数が2で実行されていることが分かります。

ただし、戻り値なしの execute で実行した場合と動作が異なり、使用できるスレッド数は2つですが、並列ではなく直列に実行されています。
戻り値を返す場合は、並列実行ではなく直列で実行されるので、複数スレッドを生成しても高速に処理が行われません。

4-3. タスクの遅延・定期実行を行う

タスクの遅延・定期実行を行うには ScheduledExecutorService インタフェースのメソッドを使用します。

遅延実行・定期実行を行うメソッド

No メソッド 戻り値 特徴
1 schedule あり 1回のみ実行するタスクの遅延実行を行うことができます。

タスクの実行には、Runnable ・ Callable どちらも指定できるので、戻り値のあり・なしを必要に応じて使い分けることができます。

2 scheduleAtFixedRate なし タスクの初回の遅延実行や、2回目以降の定期実行を行うことができます。

定期実行は、前のタスクが開始された時間からの定期実行になります。

例えば、1時間毎に定期実行する場合

1回目:09:00 開始 09:30 終了
2回目:10:00 開始 10:20 終了
3回目:11:00 開始 11:25 終了

という動作になります。

また、初回の遅延実行は引数に 0 を指定すれば、遅延なく実行できます。

タスクはRunnableで実行するため戻り値は取得できません。

3 scheduleWithFixedDelay なし タスクの初回の遅延実行や、2回目以降の定期実行を行うことができます。

定期実行は、前のタスクの終了時間からの定期実行になります。

例えば、1時間毎に定期実行する場合

1回目:09:00 開始 09:30 終了
2回目:10:30 開始 10:45 終了
3回目:11:45 開始 12:10 終了

という動作になります。

また、初回の遅延実行は引数に 0 を指定すれば、遅延なく実行できます。

タスクはRunnableで実行するため戻り値は取得できません。

4-3-1. タスクの遅延実行を行う

schedule メソッドを使用します。

schedule メソッドは、実行結果を返さない Runnableタスクと、実行結果を返す Callableタスクを実行することができます。

実行結果を返さない Runnableタスク

構文

schedule( Runnable command, long delay, TimeUnit unit )

戻り値

ScheduledFuture<?>

実行例

実行結果から、実行要求(schedule メソッドの実行)から開始まで3秒遅延していることが分かります。

また、Runnableのタスクを実行したため、戻り値は取得できず null になっていることも確認できます。

実行結果を返すCallableタスク

構文

schedule( Runnable command, long delay, TimeUnit unit )

戻り値

ScheduledFuture<?>

実行例

実行結果からタスクが遅延実行されていることが分かります。

また、Callableなタスクのため、タスクからの戻り値も取得できていることが分かります。

4-3-2. タスクの遅延・定期実行を行う( scheduleAtFixedRate )

scheduleAtFixedRate メソッドを使用した例です。

構文

scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

第1引数:実行するタスク
第2引数:初回の遅延時間
第3引数:定期実行間隔
第4引数:時間の単位( 秒や分など。第2・3引数で指定した値の単位。)

戻り値

ScheduledFuture<?>

実行例

実行結果から

・タスクの実行要求を行なってから1秒後に遅延実行
・タスクが前のタスクの開始時間から3秒おきに定期実行
・シャットダウンするとタスクが停止

ということが分かります。

4-3-3. タスクの遅延・定期実行を行う( scheduleWithFixedDelay )

scheduleWithFixedDelay メソッドを使用した例です。

構文

scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)

第1引数:実行するタスク
第2引数:初回の遅延時間
第3引数:定期実行間隔
第4引数:時間の単位( 秒や分など。第2・3引数で指定した値の単位。)

戻り値

ScheduledFuture<?>

実行例

実行結果から

・タスクの実行要求を行なってから1秒後に遅延実行
・前タスクの終了から3秒おきに定期実行
・シャットダウンするとタスクが停止

ということが分かります。

Javaでマルチスレッド処理を行う場合、上記に記載した通り様々なクラス・インタフェースを使用することが出来ます。

個人的にはスレッドプールが使える Executor がおすすめです。

スポンサーリンク

シェアする

  • このエントリーをはてなブックマークに追加

フォローする