[コピペで動く] Javaで並列計算 [コードが欲しい]

## はじめに
Java初心者です. 

## 概要
Javaで並列(並行)計算したいです. 

Threadを直接操作する?方法は教わったことがあります. [こんな感じ](http://www.javadrive.jp/applet/thread/index2.html).

しかし, java.util.concurrentを使うことでもっと楽に処理できるみたいです. やってみました. 

## コード

ParallelTest.javaというコードを書いてみました. 
8スレッド用意して, 8個のタスクを処理させます. 

8個のタスクは, それぞれ処理に要する時間が異なるように設定しました. 
タスク1は8秒, タスク2は7秒, タスク3は6秒, ..., タスク8は1秒かかります. 

```{java}
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

class ParallelTest{
  public static void main(String[] args) {
    int threadNumber = 8;
    // 8スレッド用意
    ExecutorService executor = Executors.newFixedThreadPool(threadNumber);

    // 結果を入れる配列
    String[] results = new String[threadNumber];

    // タスクのリストを作る
    List<Callable<String>> tasks = new ArrayList<Callable<String>>();
    for(int i = 1; i <= threadNumber; i++){
      tasks.add(new ParallelTasks(i));
    }

    try{
      // 並列実行
      List<Future<String>> futures;
      try{
        futures = executor.invokeAll(tasks);
      } catch(InterruptedException e){
        System.out.println(e);
        return ;
      }
      System.out.println("-----------");

      // 結果をresultsに入れる
      for(int i = 0; i< threadNumber; i++){
        try{
          results[i] = (futures.get(i)).get();
        }catch(Exception e){
          System.out.println(e);
        }
      }
    } finally{
      // 終了
      if(executor != null) executor.shutdown();

      // 結果の配列の中身
      for(String result : results) System.out.println(result);
    }
  }
}

// 並列計算する内容
class ParallelTasks implements Callable<String>{
  int taskNumber;
  public ParallelTasks(int taskNumber){
    this.taskNumber = taskNumber;
  }

  @Override
  public String call() throws Exception{
    // sleep 
    // 開始が速いタスクほど時間がかかるように設定
    Thread.sleep((long)(1000 * (8 - taskNumber) ));

    // タスクの番号を出力
    System.out.println("task " + taskNumber + " end.");
    
    return "task " + taskNumber ;
  }
}

```

出力結果. 

タスク終了の通知は, 早く終わったタスクの順番になるので, タスクの番号の逆順になっています. 
しかし, 結果の配列を呼び出すと, 元々のタスクの順番になっています. 

```{bash}
task 8 end.
task 7 end.
task 6 end.
task 5 end.
task 4 end.
task 3 end.
task 2 end.
task 1 end.
-----------
task 1
task 2
task 3
task 4
task 5
task 6
task 7
task 8

(実行時間: *** time: 8.096693 *** )
```

色々やっているように見えますが, 
Executors.newFixedThreadPool(threadNumber)でスレッドを用意して, 
タスクのリストをinvokeAllするだけです. 

結果を呼び出すときに頑張っているのがFutureです.
(futures.get(i)).get()で, i番目のタスクの処理結果を呼びます. 
このとき, 処理が終わっていない場合は, 処理が終わるまで待ってくれます. 
タスク1は8秒かかるように設定していますから, get()を行った時にはまだ終わっていないはずですが, 終了するまで待ってから結果の格納を行っています. 
なので, 結果の配列resultsには, タスクの番号順で格納されています. 
Futureは, タスクの実行状態を加味した行動をしてくれるんですね. 助かります. 

Callableがよく分からないのですが, とりあえず並列計算したい内容をcall()に書いて, 返り値の型と合わせれば動きました. 

## 感想
Java難しい.

## 参考文献
http://itpro.nikkeibp.co.jp/article/COLUMN/20071001/283395/
http://tree-tips.appspot.com/java/executorservice/

## 次に読む

Threadの詳しい話

http://qiita.com/amay077/items/b5f4e98b50d7fbcbaaec

CompleteTableFutureの話(yohhoyさんありがとうございます)

http://kiririmode.hatenablog.jp/entry/20120320/p1

Java8の話.

http://otndnld.oracle.co.jp/ondemand/javaday2014/pdf/C1-JavaDay-304328.pdf