サイトアイコン たーちゃんの「ゼロよりはいくらかましな」

【java】【ExecutorService】通常のメソッドをマルチスレッドで並列処理

javaで並列処理させようとすれば、Thread作って、startして~~となりがちなことが

多いですが、

 

JavaSE5の時代からすでにExecutorServiceという便利な代物が存在します。

 

これはThread管理をライブラリ側で肩代りしてくれるので、

実装者がThreadの生成・破棄については考慮することなく使用することができます。

 

とりわけ、普通にループしてたメソッドの性能が出ないなどの理由で、

マルチスレッドでガッとやってしまえ!!という時に絶大な効果を発揮します。

 

以下にサンプルを掲載します。

マルチスレッドで実行されるメソッド

以下のようなクラスのrunメソッドを実行するとします。

ここでは3秒待機するだけのメソッドとなります。

この部分が本来時間短縮したいメソッド処理部分のイメージです。

package jp.co.tarchan.executorservice;

/**
 * スレッドで実行したいメソッドを含むクラス.
 *
 * @author Tar-Chan
 */
public class ExecutorServiceSample {

    /**
     * 実行対象メソッド.
     */
    public void testMethod() {

        try {

            Thread.sleep(3000L);

        } catch (Exception e) {

            e.printStackTrace();

        }
    }
}

 

マルチスレッドで実行するメソッド

以下のように使用することで、マルチスレッドで実行させることが

できます。

コメントにあるように普通に10回実行すると、30秒かかるものが、

5スレッドで実行するので、全体で6秒ほどになります。

 

package jp.co.tarchan.executorservice;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.stream.IntStream;

import com.google.common.collect.Lists;

/**
 * ExecutorService実行部.
 *
 * @author Tar-Chan
 */
public class ExecutorServiceMain {

    /**
     * メイン処理.
     *
     * @param args 実行引数(今回はないよ)
     */
    public static void main(String[] args) {

        ExecutorServiceSample sample = new ExecutorServiceSample();

        // 通常10回実行すると、当然30秒かかる
        Long start = System.currentTimeMillis();

        IntStream.range(0, 10).forEach(i -> {
            sample.testMethod();
        });

        Long end = System.currentTimeMillis();

        System.out.println("sequencial process elapsed => " + (end - start) + "ms");

        // これをスレッド5個で並列実行すると・・・
        // 1スレッドあたり6秒で完結するので、
        // 全体として6秒あたりで完結することになる
        start = System.currentTimeMillis();

        // 待ち合わせリスト(Futureリスト)
        List<Future<?>> futureList = Lists.newArrayList();

        // ExecutorService スレッドは5個で実行
        ExecutorService service = Executors.newFixedThreadPool(5);

        // 同じように10回分の処理をスレッドに投入する
        // submitはcallble or runnableな何かなので、
        // ラムダで無名関数として定義した中で、
        // 対象メソッドを呼ぶ
        IntStream.range(0, 10).forEach(i -> {
            futureList.add(service.submit(() -> {
                sample.testMethod();
            }));
        });

        // Futureは完了までgetメソッドをブロックしてくれる
        // つまりここで待ち合わせをするということ
        for (Future<?> future : futureList) {

            try {

                future.get();

            } catch (Exception e) {

                e.printStackTrace();

            }
        }

        end = System.currentTimeMillis();

        System.out.println("parallel process elapsed => " + (end - start) + "ms");
    }
}

 

TPO限られるかもですが・・・

業務ロジックで性能が出ないものは、このようにマルチスレッドに

処理させることでうまくいくこともあるかもしれません。

ですが、トランザクションが絡んできたり、あるデータの状態遷移が

絡んでくる場合は、なかなかマルチスレッドによる適用が

難しい場面もあると思います。

 

マルチスレッドに動作させることが適当であるかどうかは、

TPOによるかもしれませんが、

うまくハマれば絶大な効果が得られると思います。

 


にほんブログ村


人気ブログランキング

モバイルバージョンを終了