Java5の新機能を学ぶ - java.util.concurrent

2007/10/27 | Java | コメント(0) | トラックバック(0)

スレッディングフレームワークともいうべき、java.util.concurrent。

タスク処理は、Thread、synchronized、wait/notify/notifyAllなどをうまく組み合わせてプログラミングする必要がありました。これがなかなか理解するのも使うのも難しく、苦労した覚えがあります。
Java5からは、java.util.concurrentというステキなフレームワークが追加されており、スレッド処理が格段に扱いやすくなっています。

ExecutorService

Runnableなタスクを実行するためのインタフェースとしてExecutorとExecutorServiceがあります。
Executorにはexecute()メソッドしかなく、タスクの実行をするだけ。一切制御はできない。
ExecutorServiceはタスクをキャンセルできたり、タスクの結果を取得できたり、制御するための様々なメソッドが用意されている。
ということで、通常はExecutorServiceを使うことになるのかな。

ExecutorServiceの実装クラスとしてThreadPoolExecutorがある。
その名のとおり、スレッドプールの仕組みでタスクを扱う。
//スレッドプール内のスレッド数=2としたExecutorService
ExecutorService es = Executors.newFixedThreadPool(2);
//3スレッド実行する
for (int i = 0; i < 3; i++) {
  es.submit(new RunnableTask());
}
es.shutdown();

のようにExecutorServiceはExecutorsクラスのファクトリーメソッドで取得する。
newFixedThreadPool()の他にもnewCachedThreadPool()やnewScheduledThreadPool()がある。

戻り値を返せるCallable

Executorで実行できるのはRunnableの他に、Callableもある。
Runnableと違い、Callableは戻り値を返すことができる。
CallableTaskの戻り値がStringとすると、
ExecutorService es = Executors.newCachedThreadPool();
CompletionService<String> cs = new ExecutorCompletionService(es);
//3スレッド実行する
for (int i = 0; i < 3; i++) {
  cs.submit(new CallableTask());
}
es.shutdown();
//結果を取り出す
for (int i = 0; i < 3; i++) {
  Future<String> f = cs.take();
  String s = f.get();
  System.out.println(s);
}

のようになる。
戻り値はFuture<v>で、ExecutorService#submit()の戻り値だったり、この例ではCompletionService#take()の戻り値だったりします。

スケジューリング可能なScheduledExecutorService

Timerのような○秒待って~とか○秒ごとに~処理を行わせるときには、ScheduledExecutorServiceを使う。
例えば、1秒間隔でRunnableTaskを実行し、10秒後に止めるような処理だと、
ScheduledExecutorService ses = Executors.newScheduledThreadPool(3);
//1秒おきにRunnableTaskを実行
ScheduledFuture f1
= ses.scheduleAtFixedRate(new RunnableTask(), 0,
                                       1, TimeUnit.SECONDS);
//10秒後に止める
ScheduledFuture f2 = ses.schedule(new Runnable() {
  public void run() {
    f1.cancel(true);
  }
}, 10, TimeUnit.SECONDS);
f2.get();
ses.shutdown();

のようになります。

新たな同期機構

従来のsynchronizedメソッド/ブロックを用いたモニタ機構にかわるものとして、java.util.concurrent.locksが追加されており、Java言語の同期機能がクラスとして扱えるようになった、という感じか。
Lock lock = new ReentrantLock();
lock.lock();
try {
  //同期をとってアクセスする必要のある処理
}
finally {
  lock.unlock();
}

synchronizedはブロックを抜けると自動的にロックがはずれるが、Lockの場合はきちんとfinallyでunlockしないといけない。
他にwaitやnotifyのような役割はCondition、「読み取りは排他制御しないが、書き込みは排他制御したい」といった要件のためのReadWriteLockがある。
このへんは、もうちょっと使い込みたいな。

トラックバック
トラックバックURL:
コメントをどうぞ
名前 (入力しなければ「通りすがり」):

メールアドレス (入力しても公開されません):

URL (入力すればリンクが張られます):


コメント: