目次
この記事で何がしたいか?
javaのマルチスレッドの覚書
ExecutorServiceの実行結果を記載する。
どうやって実装するか?
ExecutorServiceのAPIを使用する事で、お手軽に実装が可能です。
実行されるタスクを生成して、ExecutorServiceに投げる事で勝手にマルチ実行される。
実行タスクはRunnableをimplementsする必要があります。
良い実行例:public class MultiServiceTask implements Runnable
悪い実行例:public class MultiServiceTask
実装ソース
実行されるタスク
呼び出し(ExecutorServiceに投げて)から2.0秒後に、
自分のフィールドのタスク番号(taskNum)とスレッドID(threadID)を出力するだけのクラスです。
package multi; import java.util.logging.Level; import java.util.logging.Logger; public class MultiServiceTask implements Runnable { private static final Logger logger_ = Logger.getLogger("MultiServiceTask.class"); private int taskNum_; public void run() { try { Thread.sleep(2000L); } catch (InterruptedException e) { e.printStackTrace(); } logger_.log(Level.INFO, " taksNum[" + this.taskNum_ + "] threadID[" + Thread.currentThread().getId() + "] run()"); } MultiServiceTask(int taskNum) { this.taskNum_ = taskNum; } public int getTaskNum(){ return taskNum_; } }
実行側のExecutorService
実際に動かしてみます。
package multi; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Level; import java.util.logging.Logger; import org.junit.Test; public class MultiTest { // ロガー private static final Logger logger_ = Logger.getLogger("MultiTest.class"); // 最大スレッドプール数 private static int threadPoolSize = -1; // 実行タスク数 private static final int taskNum = 5; @Test public void test() { logger_.log(Level.INFO, "main 処理開始"); // 最大スレッドプールサイズの指定して、マルチスレッド実行 threadPoolSize =5; executeMultiThread(threadPoolSize); // 実行終了まで待つ try { Thread.sleep(9000L); //9秒 } catch (InterruptedException e) { e.printStackTrace(); } logger_.log(Level.INFO, "main 処理終了"); } private long executeMultiThread(int threadPoolSize) { long startTime = System.currentTimeMillis(); submitTask(threadPoolSize); long endTime = System.currentTimeMillis(); return endTime - startTime; } private void submitTask(int setUpThreadNum) { ExecutorService exec = Executors.newFixedThreadPool(setUpThreadNum); for (int i = 0; i < taskNum; i++) { int taskNum = i+1; MultiServiceTask service = new MultiServiceTask(taskNum); exec.submit(service); logger_.log(Level.INFO, " taskNum[" + service.getTaskNum() + "] submited"); } exec.shutdown(); } }
実行結果
今回はJUnitで実行しています。
実行タスク5:プールサイズ5
情報: taskNum[1] submited [日 7 01 22:17:29 JST 2018]
情報: taskNum[2] submited [日 7 01 22:17:29 JST 2018]
情報: taskNum[3] submited [日 7 01 22:17:29 JST 2018]
情報: taskNum[4] submited [日 7 01 22:17:29 JST 2018]
情報: taskNum[5] submited [日 7 01 22:17:29 JST 2018]
情報: taksNum[3] threadID[15] run() [日 7 01 22:17:31 JST 2018]
情報: taksNum[2] threadID[14] run() [日 7 01 22:17:31 JST 2018]
情報: taksNum[1] threadID[13] run() [日 7 01 22:17:31 JST 2018]
情報: taksNum[4] threadID[16] run() [日 7 01 22:17:31 JST 2018]
情報: taksNum[5] threadID[17] run() [日 7 01 22:17:31 JST 2018]
情報: main 処理終了 [日 7 01 22:17:39 JST 2018]
ほぼ同時にスレッドがsubmitされています。
かつほぼ同時に個々のスレッドが個別のスレッドIDで実行しているのが分かります。
また、スレッドの実行順序(taskNum)に順序性はなく、実行できる物から実行されている模様。
実行タスク5:プールサイズ1
情報: taskNum[1] submited [日 7 01 22:25:43 JST 2018]
情報: taskNum[2] submited [日 7 01 22:25:43 JST 2018]
情報: taskNum[3] submited [日 7 01 22:25:43 JST 2018]
情報: taskNum[4] submited [日 7 01 22:25:43 JST 2018]
情報: taskNum[5] submited [日 7 01 22:25:43 JST 2018]
情報: taksNum[1] threadID[13] run() [日 7 01 22:25:45 JST 2018]
情報: taksNum[2] threadID[13] run() [日 7 01 22:25:47 JST 2018]
情報: taksNum[3] threadID[13] run() [日 7 01 22:25:49 JST 2018]
情報: taksNum[4] threadID[13] run() [日 7 01 22:25:51 JST 2018]
情報: main 処理終了 [日 7 01 22:25:53 JST 2018]
情報: taksNum[5] threadID[13] run() [日 7 01 22:25:53 JST 2018]
となり、submit自体は上記同様すぐに完了しています。
しかし、スレッドの実行自体はプールサイズが1の為、submitしたタスクが待たされています。
待たされたタスクは、実行中のsubmitしたタスクが完了したら順次実行されます。
その結果、全タスクが同一スレッドIDで実行されています。
さらに、mainスレッドが終了しても生成した実行スレッドが残っている事も分かります。
mainスレッドと実行タスクの完了順序はメインスレッドが9秒、各タスクが2秒×5タスク=10秒の為、処理遅延がなければ基本的にはメインスレッドが先に完了します。