はじめに

仕事でスレッドプールを利用する機会があるので, Java でスレッドプールをつかう方法を調べてみた.

Java には, JDK1.5 から ExecutorService という便利なライブラリがあるようだ.

スレッドプールとは

はじめに, スレッドプールについての一般的な説明.

スレッドプールとは, 複数のスレッドをあらかじめ作成して待機させておき, タスクが来たら待っているスレッドにタスクを割り当てて 処理を開始させる仕組み.

スレッドの作成と破棄は, それなりにオーバーヘッドがある. そこで, スレッドは暇な時に作成しておいて, 必要になったときにスレッドに処理を割り当てることで, コンテキストスイッチ程度の軽いオーバヘッドで済ますことができる.

また, システム内のメモリは有限なので, 無限にスレッドを作成することはできない. 一度に利用できるスレッド数を制限することで, システムのリソースを制限することができるようになる.

Java でのスレッドプール実装

Executors

Executors クラスは, ExecutorService 生成のための static ファクトリーメソッドをもつ ユーティリティクラス.

主に, 以下のメソッドを用いる.


newFixedThreadPool 指定数スレッドを確保 newSingleThreadExecutor 単一スレッドを確保 newSingleThreadScheduledExecutor 指定期間おきに実行するスレッド (単一スレッド) newScheduledThreadPool 指定時間おきに実行するスレッド


ExecutorService

ExecutorService キューをもつ,スレッドプール.

主に, 以下のメソッドを用いる.


execute タスクを送信する. submit タスクの計算結果や状態を取得するための, Future オブジェクトを返す. shutdown シャットダウンを実行. 以前に送信したタスクは実行, 新規タスクは拒否


ちなみに, Executor というものもある.

ExecutorService は Executor を継承している. Executor は execute メソッドしかもたない.

Execotor にコマンドパターンを実装したものが ExecutorService.

ちなみに

ちなみに, coursera の POSA でスレッドプールの Assignment がある.

Code

import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.lang.Thread;

class SampleThreadPool {

  private static class Action implements Runnable {
    @Override
    public void run () {
      System.out.println ("Hello!!");
            try { Thread.sleep (1000);  } catch (InterruptedException e){}
    }
  }

  public static void main (String[] args) {
        // Create Single Thread Pool
    ExecutorService executor = Executors.newSingleThreadExecutor ();

        // Execute Tasks
    executor.execute (new Action ());
    executor.execute (new Action ());

        // End
    executor.shutdown ();
  }
}   

追記: 割り込み例外をあげる (2015/03/05)

割り込み例外をあげるには, 以下のような手順を踏む.

  1. execute () メソッドの代わりに submit () をコール
  2. future オブジェクトを取得
  3. future.cancel (true)
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.HashMap;
import java.lang.Thread;

class ThreadInteraptSample {

    private static class Action implements Runnable {
        public int id;

        public Action (int id) {
            this.id = id;
        }

        @Override
        public void run () {
            System.out.println ("id=" + id + " start!!");
            try {
                Thread.sleep (2000);
                System.out.println ("Time out!!");
            } catch (InterruptedException e){
                System.out.println ("Interrupted id=" + id);
            }
        }
    }

    // https://java.keicode.com/lang/multithreading-cancel.php
    public static void main (String[] args) {
        Future future1, future2, future3;

        // Create Single Thread Pool
        ExecutorService executor = Executors.newSingleThreadExecutor ();

        // Execute Tasks
        Action act1 = new Action (1);
        Action act2 = new Action (2);
        Action act3 = new Action (3);

        HashMap<Action, Future> map = new HashMap<Action, Future>();
        map.put (act1, future1 = executor.submit (act1));
        map.put (act2, future2 = executor.submit (act2));
        map.put (act3, future3 = executor.submit (act3));

        try {Thread.sleep (3000);} catch (Exception e) {}

        for (Action action: map.keySet ()) {
            if (action.id == 2) {
                map.get (action).cancel (true);
                break;
            }
        }

        executor.shutdown ();
    }
}

Bookmarks