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スレッドで処理を行います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
// Runnableインタフェースを実装したクラス class RunnableThread implements Runnable { // startメソッドが呼ばれた際に実行される処理 public void run() { for( int i = 0; i < 3; i++ ) { System.out.println("run : " + i); // 処理毎に1秒間スリープ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } // スレッド処理を呼び出すクラス public class Main { public static void main(String[] args) { // Runnableインタフェースを実装したクラス( RunnableThread )を指定してインスタンスを生成 Thread thread1 = new Thread( new RunnableThread() ); // スレッド処理を開始( 別スレッドで実行される ) thread1.start(); // メインスレッドで実行 for( int i = 0; i < 3; i++ ) { System.out.println("main : " + i ); // 実行毎に1秒間スリープ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } // 実行結果 main : 0 run : 0 run : 1 main : 1 run : 2 main : 2 |
実行結果は、処理毎に変わりますが、マルチスレッド( 実行結果の main と run )で処理が行われていることが分かります。
3. Threadクラスを使用したマルチスレッド処理
Threadクラスを使用する場合は、Threadクラスを継承したサブクラスを作成し、runメソッドをオーバーライドすることで、マルチスレッド処理を行います。
実行例
処理内容は、Runnableインタフェースと同様で、メイン・サブスレッドで 1秒毎に出力を行うプログラムです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
// Threadクラスを継承したサブクラスを作成 class SubThread extends Thread { // startメソッドが呼ばれた際に実行される処理 public void run() { for( int i = 0; i < 3; i++ ) { System.out.println("run : " + i); // 実行毎に1秒間スリープ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } // スレッド処理を呼び出すクラス public class Main { public static void main(String[] args) { // Threadクラスを継承したクラス( SubThread )でインスタンスを生成 Thread thread1 = new SubThread(); // スレッド処理を開始( 別スレッドで実行される ) thread1.start(); // メインスレッドで実行 for( int i = 0; i < 3; i++ ) { System.out.println("main : " + i ); // 実行毎に1秒間スリープ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } // 実行結果 main : 0 run : 0 run : 1 main : 1 main : 2 run : 2 |
こちらも実行結果は、処理毎に変わりますが、マルチスレッド( 実行結果の 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 メソッドを使用します。
構文
戻り値
実行例( 戻り値なし:executeメソッドでタスクを実行 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
// タスクを実行するクラス( Runnableインタフェースを実装 ) class RunnableImpl implements Runnable { public void run() { for( int i = 0; i < 3; i++ ) { System.out.println("run : " + Thread.currentThread().getName() + " : " + i ); // 実行毎に1秒スリープする try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } // タスクの実行要求を行うクラス public class Main { public static void main(String[] args) { ExecutorService execService = null; try { // シングルスレッドでタスクを処理するオブジェクトを取得 execService = Executors.newSingleThreadExecutor(); // 実行 System.out.println("タスクを3回実行"); for( int i = 0; i < 3; i++ ) { // 戻り値なし( execute )でタスクを実行 execService.execute( new RunnableImpl() ); } }finally { // ExecutorServiceは明示的に終了する必要がある execService.shutdown(); System.out.println("ExecutorService をシャットダウン"); } } } // 実行結果 タスクを3回実行 ExecutorService をシャットダウン run : pool-1-thread-1 : 0 run : pool-1-thread-1 : 1 run : pool-1-thread-1 : 2 run : pool-1-thread-1 : 0 run : pool-1-thread-1 : 1 run : pool-1-thread-1 : 2 run : pool-1-thread-1 : 0 run : pool-1-thread-1 : 1 run : pool-1-thread-1 : 2 |
実行結果からシングルスレッド( pool-1-thread-1 )で3回タスクが実行されていることが分かります。
次の実行例では、タスクの実行結果( 戻り値 )を取得します。
実行例( 戻り値あり:submitメソッドでタスクを実行 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
//タスクを実行するクラス( Callableインタフェースを実装 ) class CallableImpl implements Callable<String> { // submitメソッドが呼び出された際に行うタスク public String call() throws Exception { for( int i = 0; i < 3; i++ ) { System.out.println("run : " + Thread.currentThread().getName() + " : " + i ); // 実行毎に1秒スリープする try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } // 戻り値 return new Date().toString(); } } //タスクの実行要求を行うクラス public class Main { public static void main(String[] args) { ExecutorService execService = null; try { // シングルスレッドでタスクを処理するオブジェクトを取得 execService = Executors.newSingleThreadExecutor(); // 実行 System.out.println("タスクを3回実行"); for( int i = 0; i < 3; i++ ) { // 戻り値あり( submit )でタスクを実行 Future<String> result = execService.submit( new CallableImpl() ); // 戻り値を取得するには、Futureのgetメソッドを使う try { System.out.println(result.get()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } }finally { // ExecutorServiceは明示的に終了する必要がある execService.shutdown(); System.out.println("ExecutorService をシャットダウン"); } } } // 実行結果 タスクを3回実行 run : pool-1-thread-1 : 0 run : pool-1-thread-1 : 1 run : pool-1-thread-1 : 2 Thu Mar 03 20:38:29 JST 2022 run : pool-1-thread-1 : 0 run : pool-1-thread-1 : 1 run : pool-1-thread-1 : 2 Thu Mar 03 20:38:32 JST 2022 run : pool-1-thread-1 : 0 run : pool-1-thread-1 : 1 run : pool-1-thread-1 : 2 Thu Mar 03 20:38:35 JST 2022 ExecutorService をシャットダウン |
実行結果から、タスクから戻り値を取得できていることが分かります。
4-2. 指定した数のスレッドでタスクの並列処理を行う
newFixedThreadPool メソッドを使用します。
構文
戻り値
実行例( 戻り値なし:executeメソッドでタスクを実行 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
//タスクを実行するクラス( Runnableインタフェースを実装 ) class RunnableImpl implements Runnable { public void run() { for( int i = 0; i < 3; i++ ) { System.out.println("run : " + Thread.currentThread().getName() + " : " + i ); // 実行毎に1秒スリープする try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } //タスクの実行要求を行うクラス public class Main { public static void main(String[] args) { ExecutorService execService = null; try { // 固定数のスレッドで処理を行うオブジェクトを取得 execService = Executors.newFixedThreadPool(2); // 実行 System.out.println("タスクを3回実行"); for( int i = 0; i < 3; i++ ) { // 戻り値なし( execute )でタスクを実行 execService.execute( new RunnableImpl() ); } }finally { // ExecutorServiceは明示的に終了する必要がある execService.shutdown(); System.out.println("ExecutorService をシャットダウン"); } } } // 実行結果 タスクを3回実行 ExecutorService をシャットダウン ----- 3タスク中の2タスクが同時に実行される run : pool-1-thread-2 : 0 run : pool-1-thread-1 : 0 run : pool-1-thread-2 : 1 run : pool-1-thread-1 : 1 run : pool-1-thread-2 : 2 run : pool-1-thread-1 : 2 ----- 残りの1タスクは、タスクが先に終了したスレッドが使われ実行される run : pool-1-thread-2 : 0 run : pool-1-thread-2 : 1 run : pool-1-thread-2 : 2 |
実行結果から、最初は pool-1-thread-1 と pool-1-thread-2 が同時にタスクを実行していることが分かります。
3タスク中の2タスクは、スレッドプールのthread-1、2 が使われて並列に実行されています。
thread-2 のタスクが先に終了しているので、3タスク目は thread-2 で実行されています。
次の実行例では、タスクの実行結果( 戻り値 )を取得します。
実行例( 戻り値あり:submitメソッドでタスクを実行 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
//タスクを実行するクラス( Callableインタフェースを実装 ) class CallableImpl implements Callable<String> { // submitメソッドが呼び出された際に行うタスク public String call() throws Exception { for( int i = 0; i < 3; i++ ) { System.out.println("run : " + Thread.currentThread().getName() + " : " + i ); // 実行毎に1秒スリープする try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } // 戻り値 return new Date().toString(); } } //タスクの実行要求を行うクラス public class Main { public static void main(String[] args) { ExecutorService execService = null; try { // 固定数のスレッドで処理を行うオブジェクトを取得 execService = Executors.newFixedThreadPool(2); // 実行 System.out.println("タスクを3回実行"); for( int i = 0; i < 3; i++ ) { // 戻り値あり( submit )でタスクを実行 Future<String> result = execService.submit( new CallableImpl() ); // 戻り値を取得するには、Futureのgetメソッドを使う try { System.out.println(result.get()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } }finally { // ExecutorServiceは明示的に終了する必要がある execService.shutdown(); System.out.println("ExecutorService をシャットダウン"); } } } // 実行結果 タスクを3回実行 run : pool-1-thread-1 : 0 run : pool-1-thread-1 : 1 run : pool-1-thread-1 : 2 Fri Mar 04 08:43:56 JST 2022 run : pool-1-thread-2 : 0 run : pool-1-thread-2 : 1 run : pool-1-thread-2 : 2 Fri Mar 04 08:43:59 JST 2022 run : pool-1-thread-1 : 0 run : pool-1-thread-1 : 1 run : pool-1-thread-1 : 2 Fri Mar 04 08:44:02 JST 2022 ExecutorService をシャットダウン |
実行結果から、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 終了 という動作になります。 また、初回の遅延実行は引数に 0 を指定すれば、遅延なく実行できます。 タスクはRunnableで実行するため戻り値は取得できません。 |
3 | scheduleWithFixedDelay | なし | タスクの初回の遅延実行や、2回目以降の定期実行を行うことができます。
定期実行は、前のタスクの終了時間からの定期実行になります。 例えば、1時間毎に定期実行する場合 1回目:09:00 開始 09:30 終了 という動作になります。 また、初回の遅延実行は引数に 0 を指定すれば、遅延なく実行できます。 タスクはRunnableで実行するため戻り値は取得できません。 |
4-3-1. タスクの遅延実行を行う
schedule メソッドを使用します。
schedule メソッドは、実行結果を返さない Runnableタスクと、実行結果を返す Callableタスクを実行することができます。
実行結果を返さない Runnableタスク
構文
戻り値
実行例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
// タスクを実行するクラス( Runnableインタフェースを実装 ) class RunnableImpl implements Runnable { public void run() { System.out.println("タスクの実行開始:" + new Date() ); for( int i = 0; i < 3; i++ ) { System.out.println( "run : " + Thread.currentThread().getName() + " : " + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } // タスクの実行要求を行うクラス public class Main { public static void main(String[] args) { ScheduledExecutorService execService = null; try { // シングルスレッドで遅延実行するオブジェクトを取得 execService = Executors.newSingleThreadScheduledExecutor(); // 戻り値なし( Runnable )で 3秒後に1度だけ実行する System.out.println("タスクの実行要求:" + new Date() ); ScheduledFuture<?> runRslt = execService.schedule(new RunnableImpl(), 3, TimeUnit.SECONDS); // getメソッドで戻り値を取得 try { // Runnableは戻り値がないので、null になる System.out.println( runRslt.get() ); // null } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } }finally { if( execService != null ) { // ExecutorServiceは明示的に終了する必要がある execService.shutdown(); System.out.println("ExecutorService をシャットダウン"); } } } } // 実行結果 タスクの実行要求:Fri Mar 03 19:28:19 JST 2022 タスクの実行開始:Fri Mar 03 19:28:22 JST 2022 run : pool-1-thread-1 : 0 run : pool-1-thread-1 : 1 run : pool-1-thread-1 : 2 null ExecutorService をシャットダウン |
実行結果から、実行要求(schedule メソッドの実行)から開始まで3秒遅延していることが分かります。
また、Runnableのタスクを実行したため、戻り値は取得できず null になっていることも確認できます。
実行結果を返すCallableタスク
構文
戻り値
実行例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
// タスクを実行するクラス( Callableインタフェースを実装 ) class CallableImpl implements Callable<String> { public String call() throws Exception { System.out.println("タスクの実行開始:" + new Date() ); Thread.sleep(3000); return "タスクの実行終了:" + new Date(); } } // タスクの実行要求を行うクラス public class Main { public static void main(String[] args) { ScheduledExecutorService execService = null; try { // シングルスレッドで遅延実行するオブジェクトを取得 execService = Executors.newSingleThreadScheduledExecutor(); // 戻り値あり( Callable )で 3秒後に1度だけ実行する System.out.println("タスクの実行要求:" + new Date() ); ScheduledFuture<String> callRslt = execService.schedule(new CallableImpl(), 3, TimeUnit.SECONDS); // getメソッドで戻り値を取得 try { // Callable は実行結果が取得できる。 System.out.println( callRslt.get() ); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } }finally { if( execService != null ) { // ExecutorServiceは明示的に終了する必要がある execService.shutdown(); System.out.println("ExecutorService をシャットダウン"); } } } } // 実行結果 タスクの実行要求:Fri Mar 04 09:40:04 JST 2022 タスクの実行開始:Fri Mar 04 09:40:07 JST 2022 タスクの実行終了:Fri Mar 04 09:40:10 JST 2022 ExecutorService をシャットダウン |
実行結果からタスクが遅延実行されていることが分かります。
また、Callableなタスクのため、タスクからの戻り値も取得できていることが分かります。
4-3-2. タスクの遅延・定期実行を行う( scheduleAtFixedRate )
scheduleAtFixedRate メソッドを使用した例です。
構文
第1引数:実行するタスク
第2引数:初回の遅延時間
第3引数:定期実行間隔
第4引数:時間の単位( 秒や分など。第2・3引数で指定した値の単位。)
戻り値
実行例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
//タスクを実行するクラス( Runnableインタフェースを実装 ) class RunnableImpl implements Runnable { public void run() { System.out.println("定期タスク実行中:" + new Date()); // 実行毎に2秒スリープ try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } // タスクの実行要求を行うクラス public class Main { public static void main(String[] args) throws InterruptedException { ScheduledExecutorService execService = null; try { // シングルスレッドで遅延・定期実行するオブジェクトを取得 execService = Executors.newSingleThreadScheduledExecutor(); // 初回は1秒経過後に実行し、以降は3秒毎に実行する。 System.out.println("タスクの実行要求:" + new Date() ); execService.scheduleAtFixedRate(new RunnableImpl(), 1, 3,TimeUnit.SECONDS); // メインスレッドを20秒スリープ Thread.sleep(20000); }finally { // ExecutorServiceは明示的に終了する必要がある if( execService != null ) { execService.shutdown(); System.out.println("ExecutorService をシャットダウン:" + new Date()); } } } } // 実行結果 タスクの実行要求:Fri Mar 03 21:05:18 JST 2022 定期タスク実行中:Fri Mar 03 21:05:19 JST 2022 定期タスク実行中:Fri Mar 03 21:05:22 JST 2022 定期タスク実行中:Fri Mar 03 21:05:25 JST 2022 定期タスク実行中:Fri Mar 03 21:05:28 JST 2022 定期タスク実行中:Fri Mar 03 21:05:31 JST 2022 定期タスク実行中:Fri Mar 03 21:05:34 JST 2022 定期タスク実行中:Fri Mar 03 21:05:37 JST 2022 ExecutorService をシャットダウン:Fri Mar 03 21:05:38 JST 2022 |
実行結果から
・タスクの実行要求を行なってから1秒後に遅延実行
・タスクが前のタスクの開始時間から3秒おきに定期実行
・シャットダウンするとタスクが停止
ということが分かります。
4-3-3. タスクの遅延・定期実行を行う( scheduleWithFixedDelay )
scheduleWithFixedDelay メソッドを使用した例です。
構文
第1引数:実行するタスク
第2引数:初回の遅延時間
第3引数:定期実行間隔
第4引数:時間の単位( 秒や分など。第2・3引数で指定した値の単位。)
戻り値
実行例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
//タスクを実行するクラス( Runnableインタフェースを実装 ) class RunnableImpl implements Runnable { public void run() { System.out.print("定期タスク実行中:" + new Date()); // 実行毎に2秒スリープ try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("、実行完了:" + new Date()); } } // タスクの実行要求を行うクラス public class Main { public static void main(String[] args) throws InterruptedException { ScheduledExecutorService execService = null; try { // シングルスレッドで遅延・定期実行するオブジェクトを取得 execService = Executors.newSingleThreadScheduledExecutor(); // 初回は1秒経過後に実行し、以降は前タスクの実行完了から3秒毎に実行する。 System.out.println("タスクの実行要求:" + new Date() ); execService.scheduleWithFixedDelay(new RunnableImpl(), 1, 3,TimeUnit.SECONDS); // メインスレッドを20秒スリープ Thread.sleep(20000); }finally { // ExecutorServiceは明示的に終了する必要がある if( execService != null ) { execService.shutdown(); System.out.println("ExecutorService をシャットダウン:" + new Date()); } } } } // 実行結果 タスクの実行要求:Fri Mar 04 13:17:29 JST 2022 定期タスク実行中:Fri Mar 04 13:17:30 JST 2022、実行完了:Fri Mar 04 13:17:32 JST 2022 定期タスク実行中:Fri Mar 04 13:17:35 JST 2022、実行完了:Fri Mar 04 13:17:37 JST 2022 定期タスク実行中:Fri Mar 04 13:17:40 JST 2022、実行完了:Fri Mar 04 13:17:42 JST 2022 定期タスク実行中:Fri Mar 04 13:17:45 JST 2022、実行完了:Fri Mar 04 13:17:47 JST 2022 ExecutorService をシャットダウン:Fri Mar 04 13:17:49 JST 2022 |
実行結果から
・タスクの実行要求を行なってから1秒後に遅延実行
・前タスクの終了から3秒おきに定期実行
・シャットダウンするとタスクが停止
ということが分かります。
Javaでマルチスレッド処理を行う場合、上記に記載した通り様々なクラス・インタフェースを使用することが出来ます。
個人的にはスレッドプールが使える Executor がおすすめです。