2015年2月10日火曜日

ExecutorServiceを使ってListの集計処理

概要

Java1.6から導入されたスレッドの仕組みであるExcutorServiceを使ってfor分をスレッド化してListの集計処理をやってみました
ExecutorServiceのサンプルコード的な感覚で見ていただけると助かります

環境

  • Mac OS X 10.8.5
  • Java 1.8.0_25

サンプルコード

package test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorServiceTest {

    public ExecutorServiceTest() {

    }

    public static void main(String[] args) {
        final List<Integer> result = Collections.synchronizedList(new ArrayList<Integer>());
        ArrayList<Integer> data = new ArrayList<Integer>();
        for (int i = 0; i < 10000000; i++) {
            data.add(i);
        }
        ExecutorService exec = Executors.newFixedThreadPool(3);

        for (final int s : data) {
            exec.execute(new Runnable() {
                public void run() {
                    result.add(s);
                }
            });
        }
        // 終了まで待機する
        while(true) {
            System.out.println(data.size());
            System.out.println(result.size());
            if(data.size() == result.size()) {
                break;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // スレッドタスクの終了
        exec.shutdown();

        System.out.println("end");
    }

}

ポイント

  • スレッドの数を制御
ExecutorService exec = Executors.newFixedThreadPool(3);

ここの引数の数で最大同時スレッドの数が決定します
CPUの数やサーバリソース、ミドルウェアの設定と相談して決めましょう
Threadクラスを継承してとかRunableインタフェースを継承してとかをやる手法よりかは断然楽にスレッドを管理、操作できます

  • Listの同期化
Collections.synchronizedList(new ArrayList<Integer>());

でListを作成することでaddが別スレッドにより同時に行われてもaddの内容が失われることを防いでいる

  • スレッドの停止
exec.shutdown();

これを実行することで子スレッド側の処理を終了し、mainスレッド全体が終了します
ただ、サンプルの停止方法はかなりお行儀が悪く、ちゃんとスレッドのタイムアウトを管理して(awaitTermination)タイムアウトしたら実行中の全スレッドを停止する(shutdownNow)みたいな処理を書くのがいいみたいです
(自分はこの辺全然わかってないので詳しくは調べてみてください、すいません)

早くなったのか確認

スレッドの数を調整して計測しました
リスト内のデータはサンプルコードの通り10,000,000件を対象にしています
10回施行したその平均を取得しました
PCのスペックのスペックにより時間はだいぶブレるので参考までに見てください

スレッド数 時間(msec)
1 6835.9
3 9019
5 9077
10 9113

早くなってない

最後に

ExecutorServiceを使ってJavaのスレッドを簡単に操作してみました

実は今回の目的はfor文の高速化だったのですが、結果を見ると残念な感じになってしまいました
スレッドを使えば早くなるケースは多いと思いますが今回のようにListの集計処理では遅くなるのかもしれません
synchronizedListを使っているせいで排他制御が入ってしまいその分遅くなったとか?わからん。。。

Javaのforの高速化ってどうやるのが定石なのだろうか

0 件のコメント:

コメントを投稿