java処理の応答をよくするための手習い。 2008/03/15
2008/03/15

全体にかかる時間は、各タスクにかかる時間の総量


全体にかかる時間は、各タスクにかかる時間の総量になります。
例えば、A,B,Cと三つのタスクにわけると考えて、実行の順序に意味がある場合、つまり、タスク間に実行順序の依存がある場合は、トータルでかかる時間は、A + B + Cとなります。

Aタスクには、3秒
Bタスクには、2秒
Cタスクには、1秒
かかるとすると、計6秒かかります。



例えば、Aがあるデータベースにたいするクエリー、BはAの結果を受けて行うファイル処理、最後にCは全体の結果をまとめる、というようなロジック構成は実際にありそうです。

この場合は、各タスクの実行処理を改善しないと、応答時間はよくなりません。

簡単なコードになおすと下記のような感じになるかと思います。



public class TestTh {

public static void main(String[] args) throws InterruptedException {

long start = System.currentTimeMillis();
TestTh th = new TestTh();
System.out.println(th.longTask());
long end = System.currentTimeMillis();
System.out.println("" + ((end - start) / 1000f) + " sec.");
}

public String longTask() throws InterruptedException {

taskA();
taskB();
taskC();

return "success";
}

public void taskA() throws InterruptedException {
Thread.sleep(3000);
System.out.println("*** taskA success...");
//if(true) throw new RuntimeException("Oh!!");
}

public void taskB() throws InterruptedException {
Thread.sleep(2000);
System.out.println("*** taskB success...");
}

public void taskC() throws InterruptedException {
Thread.sleep(1000);
System.out.println("*** taskC success...");
}
}



次にタスク間に順序依存がない場合


次にタスク間に順序依存がない場合、たとえば、Aタスク、Bタスクは実行順序を逆にしても結果が同じなるような場合は、A,Bを同時に実行してもよいわけです。



この場合だと同時に実行できるA,Bタスクで時間がかかるほうがコストとなります。

Aタスクには、3秒
Bタスクには、2秒
Cタスクには、1秒
だとするとA>Bなので、
3+1で4秒がトータルとなります。

実行順序に依存関係がない場合は、スレッドを使って並行処理するとたいてい応答がよくなります(たぶん)。

jdk1.4で動作させるために、concurrentのライブラリを使ってます。


import edu.emory.mathcs.backport.java.util.concurrent.Callable;
import edu.emory.mathcs.backport.java.util.concurrent.ExecutionException;
import edu.emory.mathcs.backport.java.util.concurrent.FutureTask;
import edu.emory.mathcs.backport.java.util.concurrent.SynchronousQueue;
import edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor;
import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;


public String longTask() throws InterruptedException {

ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, 100000,
TimeUnit.SECONDS, new SynchronousQueue());
Callable callableA = new Callable() {

public Object call() throws Exception {
taskA();
return Boolean.TRUE;
}
};
Callable callableB = new Callable() {

public Object call() throws Exception {
taskB();
return Boolean.TRUE;
}
};

List list = new ArrayList();
list.add(callableA);
list.add(callableB);
try {
List resuList = executor.invokeAll(list);

// タスクAとタスクBが終了したのちに以下、実行。
Iterator iterator = resuList.iterator();
while (iterator.hasNext()) {
FutureTask t = (FutureTask) iterator.next();
try {
System.out.println(t.get());
} catch (ExecutionException e) {
// e.printStackTrace();
}
}
} finally {
executor.shutdown();
}

taskC();

return "success";
}


まとめ


図でまとめるとこんな感じかな。


タスクの実行結果に順番がない場合(依存がない場合)は、スレッドを使って、応答をよくすることができます。
あっ、なんでもかんでもスレッドにのせていいわけではないですし、もちろんスレッドを生成するコストより順番に実行した結果時間がかかる場合にのみ有効です。

java5から導入されたconcurrent系のライブラリを念頭に入れて、1.4環境ならbackportのライブラリを使うのもありかなと思います。

: