SlideShare ist ein Scribd-Unternehmen logo
1 von 36
Downloaden Sie, um offline zu lesen
Java並行処理プログラミング
―その「基盤」と「最新API」を究める―
Java Concurrency in Practice
社内勉強会資料
第6章 タスクの実行
大槌剛彦 (@ohtsuchi)
6章の内容
•(6-1) 2つの方式の紹介とその欠点
・sequential approach
poor responsiveness and throughput
・thread per task approach
poor resource management
•(6-2) Executor Framework
–実行ポリシー
–スレッドプール
–Executorのライフサイクル
–タスクの遅延開始と周期的実行
・(6-3)並列化できる箇所/すべき箇所を見つける
–ページレンダラの例
–Callable and Future
–異質なタスクを並列化する限界
–CompletionService
–タスクに時間制限を設ける
6-1. タスクをスレッドで実行する
•最初のステップ
–タスク境界(task boundaries)を見つけること
•タスク
–独立した活動
–他のタスクの結果に依存しない
サーバアプリケーションの場合
•個々の client リクエスト
–独立性
–適切なサイズ
6-1-1 タスクを逐次的に実行する
•List 6-1 Sequential Web Server.
–1度に1つのリクエストしか扱えない
–サーバが1つのリクエストを処理する間、新たな接続は待たされる
–スループットも応答性も得られない
–GUIフレームワークでは、single thread で sequentially なタスクに利点
(9章)
class SingleThreadWebServer {
public static void main(String[] args) throws IOException {
ServerSocket socket = new ServerSocket(80);
while (true) {
Socket connection = socket.accept();
handleRequest(connection);
}
6-1-2. タスクのためのスレッドを明示的に作る
•List 6-2 Web Server that Starts a New Thread for Each Request.
–main loop が クライアントからの接続毎に new thread を作る
•new thread がリクエストを処理
•main thread はリクエストを処理しない
class ThreadPerTaskWebServer {
public static void main(String[] args) throws IOException {
ServerSocket socket = new ServerSocket(80);
while (true) {
final Socket connection = socket.accept();
Runnable task = new Runnable() {
public void run() {
handleRequest(connection);
}
};
new Thread(task).start();
}
6-1-2. タスクのためのスレッドを明示的に作る
•3つの結果
–応答性の向上※
•main はタスクの処理を行わないため。
•前の処理が完了する前に、新しい接続を受け付ける事ができるた
め。
–スループットの向上※
•プロセッサが複数あれば
•タスクを並列に処理できるため
–タスクを処理するコードはスレッドセーフである必要
•複数のタスクのために並行に起動するため
※負荷が重くない時、リクエストがサーバの処理能力を超えない時
6-1-3 制限のないスレッド作成の欠点
•スレッドのライフサイクルのオーバヘッド
–スレッドの作成と廃棄はただではない
•リソースの消費
–特にメモリ。
–実行状態のスレッドの数がプロセッサの数より多いと、スレッドはidle状態
になる
–idle状態のスレッドが多いと、メモリ消費量が増えて、garbage collector
の負担が大きくなる
–全てのCPUがbusyなら、それ以上スレッドを作るのは意味が無いし、サ
ーバの能力が劣化する
•安定性
–作れるスレッドの数にはlimitがある
–limit に達した場合、結果多くの場合、OutOfMemoryError
–OOMEからのリカバリはとてもrisky 。このlimitに達しないようなプログラ
ムを構造する方がずっと容易。
この方式の問題点:スレッドを作る数に制限が無いこと
6-2 Executor フレームワーク
•5章(5-3 p102)で サイズ制限のあるキュー (bounded queues)
を使用した
–過負荷でメモリ不足にならないために
–Thread pool はそれと同じ考え方でスレッドを管理する
•java.util.concurrent パッケージは、Executor フレームワークの
一部として、柔軟なThread poolの実装を提供
6-2 Executor フレームワーク
•List 6-3 Executor Interface.
•taskの submission(依頼) と execution(実行) を分離
–producer consumer pattern (5-3)
public interface Executor {
void execute(Runnable command);
}
6-2-1Executor を使ったWebサーバ
• List 6-4. Web Server Using a Thread Pool.
class TaskExecutionWebServer {
private static final int NTHREADS = 100;
private static final Executor exec
= Executors.newFixedThreadPool(NTHREADS);
public static void main(String[] args) throws IOException {
ServerSocket socket = new ServerSocket(80);
while (true) {
final Socket connection = socket.accept();
Runnable task = new Runnable() {
public void run() {
handleRequest(connection);
}
};
exec.execute(task);
}
6-2-1Executor を使ったWebサーバ
•thread per task approach の動作にするExecutorの例
–List 6-5
•sequential approach の動作にするExecutorの例
–List 6-6
public class ThreadPerTaskExecutor implements Executor {
public void execute(Runnable r) {
new Thread(r).start();
};
}
public class WithinThreadExecutor implements Executor {
public void execute(Runnable r) {
r.run();
};
}
6-2-2 実行ポリシー
•タスクの実行の what, where, when, how
–タスクを
•どのスレッドで実行するか?
•どんな順序で実行するか?(FIFO, LIFO, priority order)
•並行に実行する数は?
•キューに並ばす最大数は?
•過負荷でrejectしなければいけない時にどのタスクを犠牲にする
か? アプリケーションの通知はどうするか?
•実行前後でどんなアクションを行うべきか?
•実行ポリシーの仕様を、タスクの依頼から分離
–実働機 のリソース状況にマッチした実行ポリシーを指定することが困難ではなく
なる。
6-2-3 スレッドプール
•worker threads の均質な(一種類のスレッドから成る)
プールを管理
•利点
–既存の thread の再利用
–thread 作成の待ち時間がタスクの実行を遅らせない→
応答性向上
–thread pool サイズの適切な tuning
•→ プロセッサを無駄に遊ばせない
•→ メモリ不足にならない
6-2-3 スレッドプール
Executors の static ファクトリメソッド
•newFixedThreadPool
•固定サイズ
•スレッドを最大プールサイズまで作る
•プールサイズを一定に保つ
•return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new
LinkedBlockingQueue<Runnable>())
•newCachedThreadPool
•more flexibility
•idle threads は刈り取る
•要求が増えたら new threads を追加
•プールのサイズ制限が無い
•return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new
SynchronousQueue<Runnable>());
6-2-3 スレッドプール
Executors の static ファクトリメソッド
•newSingleThreadExecutor
•single worker thread
•task queue の順序(FIFO, LIFO, priority order)に応じて、 sequentially に処理す
る事が保証されている。
•return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
•newScheduledThreadPool
•固定サイズ
•遅延開始、周期的実行をサポート
•Timerに似ている (→ 6-2-5 参照)
•return new ScheduledThreadPoolExecutor(corePoolSize);
→ThreadPoolExecutorの構成については第8章参照
6-2-4 Executor のライフサイクル
•graceful shutdown
•開始した仕事は無事に終わるが、新たな仕事は受け付けない
•abrupt shutdown
•唐突なshutdown。電源OFF。
•ExecutorService
•Executor を extends した interface
•ライフサイクルを管理するメソッドが定義されている
•List 6-7. Lifecycle Methods in ExecutorService.
public interface ExecutorService extends Executor {
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
6-2-4 Executor のライフサイクル
ExecutorService が暗黙的に定義しているライフサイクル
•ExecutorService の ライフサイクルの3つのステート
•running
•shutting down
•terminated
•shutdown メソッド
•graceful shutdown
•新たなタスクは受け付けない
•前に依頼されたタスク(実行を開始していないものも含めて)は完了可能
→ 終了するまで待つ
•shutdownNow メソッド
•abrupt shutdown
•実行中のタスクのキャンセルを試みる
•キューにあってまだ始まっていないタスクはスタートしない
6-2-4 Executor のライフサイクル
•シャットダウンの後に依頼されたタスク
•rejected execution handler (8-3-3)が取り扱う
•黙ってタスクを捨てる
or
•RejectedExecutionException をthrowする
•全てのタスクが完了したら
→ ExecutorService は terminated ステートに遷移
•awaitTermination メソッド
•ExecutorService が terminated ステートへ到達するのを待つ
•(apiリファレンス) 「シャットダウン要求後にすべてのタスクが実行を完了していたか、タイムアウトが発生するか、現
在のスレッドで割り込みが発生するか、そのいずれかが最初に発生するまでブロックします」
•isTerminated メソッド
•ExecutorService が terminated ステートへ到達したか否かをチェック
•(apiリファレンス)「シャットダウンに続いてすべてのタスクが完了していた場合、true を返します。 」
•shutdown に続いて awaitTermination を呼ぶのはよく行われる
→ シャットダウンに関しては第7章で
6-2-4 Executor のライフサイクル
•ライフサイクルのサポートを加えた Webサーバの例
•List 6-8. Web Server with Shutdown Support.
class LifecycleWebServer {
private final ExecutorService exec = ...;
public void start() throws IOException {
ServerSocket socket = new ServerSocket(80);
while (!exec.isShutdown()) {
try {
final Socket conn = socket.accept();
exec.execute(new Runnable() {
public void run() { handleRequest(conn);
});
} catch (RejectedExecutionException e) {
if (!exec.isShutdown())
log("task submission rejected", e);
}
}
}
public void stop() { exec.shutdown(); }
void handleRequest(Socket connection) {
Request req = readRequest(connection);
if (isShutdownRequest(req))
stop();
else
dispatchRequest(req);
}
6-2-5 タスクの遅延開始と周期的実行
•(×)java.util.Timer にはいくつか欠点がある
→ (○)ScheduledThreadPoolExecutor を代わりに使うように。
•Timerの欠点
•1つのTimerで1つのスレッド。
•複数のTimerTaskを実行する時
→ 1つの TimerTask の実行が長すぎると
→ 他のタスクが不正確になる
※ ScheduledThreadPoolExecutor は複数スレッドを使って回避する
•システムクロックの変更に影響を受けてしまう
•TimerTask が非チェック例外をthrowした時の対応がお粗末
•Timer スレッドがcatchしない
•Timer スレッドが終了してしまう。Timer全体がキャンセルされたと誤判断
•"thread leakage" と呼ばれる問題
•(スケジューリング済みで未実行のタスクが実行されない、新たなタスクはスケジュールされない)
•→ 7-3 で避ける方法を説明
6-2-5 タスクの遅延開始と周期的実行
•List 6-9 Timer の動作をデモするクラス
•DelayQueue
•scheduling service を自作する場合に使える
•BlockingQueue の実装クラス
•ScheduledThreadPoolExecutorのようなスケジューリング機能
public class OutOfTime {
public static void main(String[] args) throws Exception {
Timer timer = new Timer();
timer.schedule(new ThrowTask(), 1);
SECONDS.sleep(1);
timer.schedule(new ThrowTask(), 1); // “Timer already cancelled”
SECONDS.sleep(5);
}
static class ThrowTask extends TimerTask {
public void run() { throw new RuntimeException(); }
}
6-3 並列化できる箇所/すべき箇所を見つける
•タスク境界(task boundary)が自明な場合
•server applications の 1つのclient request
•1つのclient requestの中にさらに並列できる部分がある (DBサーバなど)
•6-3-1 Example: Sequential Page Renderer
•一番単純
•長時間待たされる
6-3-2 結果を返すタスク : Callable and Future
•Runnable run メソッド
•値を返せない
•チェックされる例外をthrowできない
•Callable call メソッド
•値を返せる
•チェックされる例外をthrowできる
•Executor が実行するタスクのlifecycle
•4つの段階:
•created
•submitted
•started
•completed
•Executor framework でのタスクのキャンセル (※キャンセルについては第7章で)
•submit されたけれど、started になってないタスク
→ いつでもキャンセル可能
•started のタスク
→ interruption に応答する場合キャンセル可能
•completed のタスク
→ 何も起きない
6-3-2 結果を返すタスク : Callable and Future
•Future
•タスクのlifecycleを表現
•前に進めるだけ、後戻りはできない
•ExecutorService のlifecycleと同様
•調べるメソッド
•タスクが completed かどうか
•タスクが cancel されたかどうか
•タスクの結果を取り出すメソッド
•List 6-11 Callable and Future Interfaces.
public interface Callable<V> {
V call() throws Exception;
}
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException,
CancellationException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException,
CancellationException, TimeoutException;
6-3-2 結果を返すタスク : Callable and Future
•Future.get の振る舞い
•タスクのstateによって異なる
•completed
•直ちにreturnする
or
•Exceptionをthrow
•完了していなければ、完了するまでブロック
•Exceptionをthrowして完了の場合
•get は ExecutionException でラップしてrethrowする
→ getCause メソッドで元の例外を取得できる
•cancelled
•get は CancellationException を throwする
6-3-2 結果を返すタスク : Callable and Future
•Future を作成する方法
•ExecutorService の submit メソッド
•FutureTask のインスタンスを明示的に作成
•AbstractExecutorService の newTaskFor メソッド
•defaultの実装は FutureTask をnewしてるだけ
•overrideする事でFuture のインスタンスの作り方を制御できる(java6から)
•List 6-12 Default Implementation of newTaskFor in ThreadPoolExecutor.
(参考: クラス図)
http://blog.thihy.info/wp-content/uploads/2013/01/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E7%BB%A7%E6%89%BF%E5%85%B3%E7%B3%BB.jpg
(参考: FutureTaskクラスがJava SE 6で変更)
http://itpro.nikkeibp.co.jp/article/COLUMN/20071001/283396/
protected <T> RunnableFuture<T> newTaskFor(Callable<T> task) {
return new FutureTask<T>(task);
}
6-3-3 例: Future を使うページレンダラ
•2つのタスクに分割
•テキストのrender
•画像のダウンロード → Callable を作って → ExecutorService に submit
•List 6-13 Waiting for Image Download with Future.
public class FutureRenderer {
private final ExecutorService executor = ...;
void renderPage(CharSequence source) {
final List<ImageInfo> imageInfos = scanForImageInfo(source);
Callable<List<ImageData>> task =
new Callable<List<ImageData>>() {
public List<ImageData> call() {
List<ImageData> result
= new ArrayList<ImageData>();
for (ImageInfo imageInfo : imageInfos)
result.add(imageInfo.downloadImage());
return result;
}
};
Future<List<ImageData>> future = executor.submit(task);
renderText(source);
try {
List<ImageData> imageData = future.get();
for (ImageData data : imageData)
renderImage(data);
} catch (InterruptedException e) {
// thread の interrupted status を再確立する
Thread.currentThread().interrupt();
// 結果はいらないので、タスクをcancel
future.cancel(true);
} catch (ExecutionException e) {
throw launderThrowable(e.getCause());
6-3-4 異質なタスクを並列化する限界
前の例(FutureRenderer) では
•テキスト処理の時間 < 画像処理の時間
•実行性能は逐次処理とあまり変わらない
•しかし、コードは逐次処理よりも複雑に
→ 苦労の成果にも限界
→ 成果が上がるのは同質のタスクの場合
6-3-5 CompletionService:
Executor Meets BlockingQueue
•複数タスクの完了
•タイムアウトが0の Future.get で何度もポーリング
↓ もっと良い方法
•CompletionService を使う
•take や poll メソッドが使える
•ExecutorCompletionService
•CompletionService の実装クラス
6-3-5 CompletionService:
•ExecutorCompletionService のソースの抜粋
public class ExecutorCompletionService<V> implements CompletionService<V> {
private final Executor executor;
private final AbstractExecutorService aes;
private final BlockingQueue<Future<V>> completionQueue;
private class QueueingFuture extends FutureTask<Void> {
QueueingFuture(RunnableFuture<V> task) {
super(task, null);
this.task = task;
}
protected void done() { completionQueue.add(task); } // FutureTask にはtaskが完了したら呼ばれるdoneメソッドがある
private final Future<V> task;
}
…
public ExecutorCompletionService(Executor executor) {
if (executor == null)
throw new NullPointerException();
this.executor = executor;
…
public Future<V> submit(Callable<V> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task); // return new FutureTask<V>(task);
// taskの実行をexecutorに委譲 // taskが依頼されるとtaskはQueueingFutureでラップされる
executor.execute(new QueueingFuture(f));
return f;
}
…
public Future<V> take() throws InterruptedException {
return completionQueue.take();
}
public Future<V> poll() {
return completionQueue.poll();
}
6-3-6 CompletionService を使ったページレンダラ
•List 6-15
•画像1つ1つのダウンロード → 別々のタスク → スレッドプールで実行
public class Renderer {
private final ExecutorService executor;
Renderer(ExecutorService executor) { this.executor = executor; }
void renderPage(CharSequence source) {
final List<ImageInfo> info = scanForImageInfo(source);
CompletionService<ImageData> completionService =
new ExecutorCompletionService<ImageData>(executor);
for (final ImageInfo imageInfo : info)
completionService.submit(new Callable<ImageData>() {
public ImageData call() {
return imageInfo.downloadImage();
}
});
renderText(source);
try {
for (int t = 0, n = info.size(); t < n; t++) {
// takeで取り出す順番は、タスクが完了した順番 // 何も存在しない場合は待機
Future<ImageData> f = completionService.take();
ImageData imageData = f.get();
renderImage(imageData);
}
…
6-3-7 タスクに時間制限を設ける
•例: 広告配信
•ADサーバから2秒以内に取り込めなかったら
•→ デフォルトの広告を表示
•→ 広告の取り込みは中断
•時間指定付きの Future.get
•結果が得られば直ちにreturnする
•タイムアウトまで得られなければ TimeoutException を throw
•get に渡すタイムアウトが負数の場合は0扱い
•タスクのキャンセル
•TimeoutException を catch → future.cancel(true);
→ List 6-13, 6-16 で使用
※第7章
6-3-7 タスクに時間制限を設ける
•List 6-16 Fetching an Advertisement with a Time Budget.
•広告を取り込むタスク → Executorに依頼
Page renderPageWithAd() throws InterruptedException {
long endNanos = System.nanoTime() + TIME_BUDGET;
Future<Ad> f = exec.submit(new FetchAdTask());
// ページを Render しながら 広告を待つ
Page page = renderPageBody();
Ad ad;
try {
// 一定の時間(TIME_BUDGET)だけ待つ
long timeLeft = endNanos - System.nanoTime();
ad = f.get(timeLeft, NANOSECONDS);
} catch (ExecutionException e) {
ad = DEFAULT_AD;
} catch (TimeoutException e) {
ad = DEFAULT_AD;
f.cancel(true);
}
page.setAd(ad);
return page;
6-3-8 例: 旅行予約ポータル
•例: 複数企業からビッドを取り込む
•一定時間内の応答だけ提示
•タスク境界
•1つの起業からのビッド → 独立している → 妥当なタスク境界
•複数企業の数分、タスクをn個
•スレッドプールに依頼
•複数のFutureで管理
•時間指定付きの Future.get
•結果を逐次的に取り込む
↓もっと簡単な方法
•時間指定付きの invokeAll
6-3-8 例: 旅行予約ポータル
•invokeAll
•引数 : a collection of tasks
•戻り値: a collection of Futures
•この2つのcollectionは構造が同じ。
•引数のタスクのcollectionのiteratorの順番で戻り値のcollectionにFutureが加わる
•時間指定付きの invokeAll がリターンするのは
•全てのタスクが完了したとき
•呼び出しているスレッドがinterruptされた時
•タイムアウトになった時
→ 各タスクは
•正常に完了している
or
•キャンセルされている 、かのどちらか。
→ get または isCancelled を読んでどちらの結果なのか判別できる
6-3-8 例: 旅行予約ポータル
•List 6-17 時間制限付きで旅行の見積もりをリクエストする (抜粋)
private class QuoteTask implements Callable<TravelQuote> {
…
public List<TravelQuote> getRankedTravelQuotes( …) {
…
List<QuoteTask> tasks = new ArrayList<QuoteTask>();
for (TravelCompany company : companies)
tasks.add(new QuoteTask(company, travelInfo));
List<Future<TravelQuote>> futures =
exec.invokeAll(tasks, time, unit);
List<TravelQuote> quotes =
new ArrayList<TravelQuote>(tasks.size());
Iterator<QuoteTask> taskIter = tasks.iterator();
for (Future<TravelQuote> f : futures) {
QuoteTask task = taskIter.next();
try {
quotes.add(f.get());
} catch (ExecutionException e) {
quotes.add(task.getFailureQuote(e.getCause()));
} catch (CancellationException e) {
quotes.add(task.getTimeoutQuote(e));
}
}
Collections.sort(quotes, ranking);
return quotes;

Weitere ähnliche Inhalte

Was ist angesagt?

Linuxのプロセススケジューラ(Reading the Linux process scheduler)
Linuxのプロセススケジューラ(Reading the Linux process scheduler)Linuxのプロセススケジューラ(Reading the Linux process scheduler)
Linuxのプロセススケジューラ(Reading the Linux process scheduler)
Hiraku Toyooka
 
Completely Fair Scheduler (CFS)
Completely Fair Scheduler (CFS)Completely Fair Scheduler (CFS)
Completely Fair Scheduler (CFS)
gokzy
 
Mod lua
Mod luaMod lua
Mod lua
do_aki
 
位置情報を使ったサービス「スマポ」をPostgreSQLで作ってみた db tech showcase 2013 Tokyo
位置情報を使ったサービス「スマポ」をPostgreSQLで作ってみた db tech showcase 2013 Tokyo位置情報を使ったサービス「スマポ」をPostgreSQLで作ってみた db tech showcase 2013 Tokyo
位置情報を使ったサービス「スマポ」をPostgreSQLで作ってみた db tech showcase 2013 Tokyo
Yoshiyuki Asaba
 

Was ist angesagt? (20)

システムパフォーマンス勉強会#8
システムパフォーマンス勉強会#8システムパフォーマンス勉強会#8
システムパフォーマンス勉強会#8
 
Ruby on Rails 4.0 勉強会資料
Ruby on Rails 4.0 勉強会資料Ruby on Rails 4.0 勉強会資料
Ruby on Rails 4.0 勉強会資料
 
Postgres Toolkitのご紹介
Postgres Toolkitのご紹介Postgres Toolkitのご紹介
Postgres Toolkitのご紹介
 
Linuxのプロセススケジューラ(Reading the Linux process scheduler)
Linuxのプロセススケジューラ(Reading the Linux process scheduler)Linuxのプロセススケジューラ(Reading the Linux process scheduler)
Linuxのプロセススケジューラ(Reading the Linux process scheduler)
 
SQL大量発行処理をいかにして高速化するか
SQL大量発行処理をいかにして高速化するかSQL大量発行処理をいかにして高速化するか
SQL大量発行処理をいかにして高速化するか
 
Completely Fair Scheduler (CFS)
Completely Fair Scheduler (CFS)Completely Fair Scheduler (CFS)
Completely Fair Scheduler (CFS)
 
PL/Pythonで独自の集約関数を作ってみる
PL/Pythonで独自の集約関数を作ってみるPL/Pythonで独自の集約関数を作ってみる
PL/Pythonで独自の集約関数を作ってみる
 
mercurial-users.jp speech at OSC2013 Tokyo/Spring
mercurial-users.jp speech at OSC2013 Tokyo/Springmercurial-users.jp speech at OSC2013 Tokyo/Spring
mercurial-users.jp speech at OSC2013 Tokyo/Spring
 
【学習メモ#9th】12ステップで作る組込みOS自作入門
【学習メモ#9th】12ステップで作る組込みOS自作入門 【学習メモ#9th】12ステップで作る組込みOS自作入門
【学習メモ#9th】12ステップで作る組込みOS自作入門
 
Linux女子会 - お仕事メリハリ術♪(プロセススケジューラ編)
Linux女子会 - お仕事メリハリ術♪(プロセススケジューラ編)Linux女子会 - お仕事メリハリ術♪(プロセススケジューラ編)
Linux女子会 - お仕事メリハリ術♪(プロセススケジューラ編)
 
【学習メモ#8th】12ステップで作る組込みOS自作入門
【学習メモ#8th】12ステップで作る組込みOS自作入門 【学習メモ#8th】12ステップで作る組込みOS自作入門
【学習メモ#8th】12ステップで作る組込みOS自作入門
 
PostgreSQLセキュリティ総復習
PostgreSQLセキュリティ総復習PostgreSQLセキュリティ総復習
PostgreSQLセキュリティ総復習
 
PostgreSQL運用管理入門
PostgreSQL運用管理入門PostgreSQL運用管理入門
PostgreSQL運用管理入門
 
トランザクションの並行処理制御
トランザクションの並行処理制御トランザクションの並行処理制御
トランザクションの並行処理制御
 
MySQLメインの人がPostgreSQLのベンチマークをしてみた話
MySQLメインの人がPostgreSQLのベンチマークをしてみた話MySQLメインの人がPostgreSQLのベンチマークをしてみた話
MySQLメインの人がPostgreSQLのベンチマークをしてみた話
 
「今そこにある危機」を捉える ~ pg_stat_statements revisited
「今そこにある危機」を捉える ~ pg_stat_statements revisited「今そこにある危機」を捉える ~ pg_stat_statements revisited
「今そこにある危機」を捉える ~ pg_stat_statements revisited
 
Mod lua
Mod luaMod lua
Mod lua
 
位置情報を使ったサービス「スマポ」をPostgreSQLで作ってみた db tech showcase 2013 Tokyo
位置情報を使ったサービス「スマポ」をPostgreSQLで作ってみた db tech showcase 2013 Tokyo位置情報を使ったサービス「スマポ」をPostgreSQLで作ってみた db tech showcase 2013 Tokyo
位置情報を使ったサービス「スマポ」をPostgreSQLで作ってみた db tech showcase 2013 Tokyo
 
ゆるふわMySQLフェイルオーバー
ゆるふわMySQLフェイルオーバーゆるふわMySQLフェイルオーバー
ゆるふわMySQLフェイルオーバー
 
PostgreSQLアーキテクチャ入門
PostgreSQLアーキテクチャ入門PostgreSQLアーキテクチャ入門
PostgreSQLアーキテクチャ入門
 

Andere mochten auch (6)

Kanor
KanorKanor
Kanor
 
Qr stuff
Qr stuffQr stuff
Qr stuff
 
Terpadu
TerpaduTerpadu
Terpadu
 
Osc office 365_seminar_15th_feb_2012_public
Osc office 365_seminar_15th_feb_2012_publicOsc office 365_seminar_15th_feb_2012_public
Osc office 365_seminar_15th_feb_2012_public
 
Tokyo
TokyoTokyo
Tokyo
 
Osc share point 2010 information management seminar - 17th feb 2012 - public
Osc   share point 2010 information management seminar - 17th feb 2012 - publicOsc   share point 2010 information management seminar - 17th feb 2012 - public
Osc share point 2010 information management seminar - 17th feb 2012 - public
 

Ähnlich wie Java concurrency in_practice_chap06

Continuous delivery 6
Continuous delivery 6Continuous delivery 6
Continuous delivery 6
ShinyaOzawa
 
COD2012 C3 : SQL Server 2012で振り返る、SQLOSのスレッド スケジューリング
COD2012 C3 : SQL Server 2012で振り返る、SQLOSのスレッド スケジューリングCOD2012 C3 : SQL Server 2012で振り返る、SQLOSのスレッド スケジューリング
COD2012 C3 : SQL Server 2012で振り返る、SQLOSのスレッド スケジューリング
Masayuki Ozawa
 
プロとしてのOracleアーキテクチャ入門 ~番外編~
プロとしてのOracleアーキテクチャ入門 ~番外編~プロとしてのOracleアーキテクチャ入門 ~番外編~
プロとしてのOracleアーキテクチャ入門 ~番外編~
ryouta watabe
 
継続的デリバリー読書会 第 5 章 デプロイメントパイプラインの解剖学
継続的デリバリー読書会 第 5 章 デプロイメントパイプラインの解剖学継続的デリバリー読書会 第 5 章 デプロイメントパイプラインの解剖学
継続的デリバリー読書会 第 5 章 デプロイメントパイプラインの解剖学
Takuma SHIRAISHI
 

Ähnlich wie Java concurrency in_practice_chap06 (20)

Java Day Tokyo 2014 まとめ (chen)
Java Day Tokyo 2014 まとめ (chen)Java Day Tokyo 2014 まとめ (chen)
Java Day Tokyo 2014 まとめ (chen)
 
第2回 松本勉強会 2012 05 25 - apache2.4とmod_lua
第2回 松本勉強会 2012 05 25 - apache2.4とmod_lua第2回 松本勉強会 2012 05 25 - apache2.4とmod_lua
第2回 松本勉強会 2012 05 25 - apache2.4とmod_lua
 
Oracle コンサルいらずのチューニングことはじめ
Oracle コンサルいらずのチューニングことはじめOracle コンサルいらずのチューニングことはじめ
Oracle コンサルいらずのチューニングことはじめ
 
「Oracle Database + Java + Linux」 環境における性能問題の調査手法 ~ミッションクリティカルシステムの現場から~ Part.1
「Oracle Database + Java + Linux」環境における性能問題の調査手法 ~ミッションクリティカルシステムの現場から~ Part.1「Oracle Database + Java + Linux」環境における性能問題の調査手法 ~ミッションクリティカルシステムの現場から~ Part.1
「Oracle Database + Java + Linux」 環境における性能問題の調査手法 ~ミッションクリティカルシステムの現場から~ Part.1
 
Rubyにおけるトレース機構の刷新
Rubyにおけるトレース機構の刷新Rubyにおけるトレース機構の刷新
Rubyにおけるトレース機構の刷新
 
Continuous delivery 6
Continuous delivery 6Continuous delivery 6
Continuous delivery 6
 
企業におけるSpring@日本springユーザー会20090624
企業におけるSpring@日本springユーザー会20090624企業におけるSpring@日本springユーザー会20090624
企業におけるSpring@日本springユーザー会20090624
 
Jjug springセッション
Jjug springセッションJjug springセッション
Jjug springセッション
 
IT Pro のための PowerShell スクリプティング
IT Pro のための PowerShell スクリプティングIT Pro のための PowerShell スクリプティング
IT Pro のための PowerShell スクリプティング
 
COD2012 C3 : SQL Server 2012で振り返る、SQLOSのスレッド スケジューリング
COD2012 C3 : SQL Server 2012で振り返る、SQLOSのスレッド スケジューリングCOD2012 C3 : SQL Server 2012で振り返る、SQLOSのスレッド スケジューリング
COD2012 C3 : SQL Server 2012で振り返る、SQLOSのスレッド スケジューリング
 
What's new in open shift container platform 4.7 japan_20210318
What's new in open shift container platform 4.7 japan_20210318What's new in open shift container platform 4.7 japan_20210318
What's new in open shift container platform 4.7 japan_20210318
 
Slurmのジョブスケジューリングと実装
Slurmのジョブスケジューリングと実装Slurmのジョブスケジューリングと実装
Slurmのジョブスケジューリングと実装
 
Linux女子部 systemd徹底入門
Linux女子部 systemd徹底入門Linux女子部 systemd徹底入門
Linux女子部 systemd徹底入門
 
プロとしてのOracleアーキテクチャ入門 ~番外編~
プロとしてのOracleアーキテクチャ入門 ~番外編~プロとしてのOracleアーキテクチャ入門 ~番外編~
プロとしてのOracleアーキテクチャ入門 ~番外編~
 
Using docker infrastructure
Using docker infrastructureUsing docker infrastructure
Using docker infrastructure
 
FIWAREシステム内の短期履歴の管理
FIWAREシステム内の短期履歴の管理FIWAREシステム内の短期履歴の管理
FIWAREシステム内の短期履歴の管理
 
Atc15_reading_networking_session
Atc15_reading_networking_sessionAtc15_reading_networking_session
Atc15_reading_networking_session
 
Javaの進化にともなう運用性の向上はシステム設計にどういう変化をもたらすのか
Javaの進化にともなう運用性の向上はシステム設計にどういう変化をもたらすのかJavaの進化にともなう運用性の向上はシステム設計にどういう変化をもたらすのか
Javaの進化にともなう運用性の向上はシステム設計にどういう変化をもたらすのか
 
継続的デリバリー読書会 第 5 章 デプロイメントパイプラインの解剖学
継続的デリバリー読書会 第 5 章 デプロイメントパイプラインの解剖学継続的デリバリー読書会 第 5 章 デプロイメントパイプラインの解剖学
継続的デリバリー読書会 第 5 章 デプロイメントパイプラインの解剖学
 
Jbatch実践入門 #jdt2015
Jbatch実践入門 #jdt2015Jbatch実践入門 #jdt2015
Jbatch実践入門 #jdt2015
 

Java concurrency in_practice_chap06

  • 1. Java並行処理プログラミング ―その「基盤」と「最新API」を究める― Java Concurrency in Practice 社内勉強会資料 第6章 タスクの実行 大槌剛彦 (@ohtsuchi)
  • 2. 6章の内容 •(6-1) 2つの方式の紹介とその欠点 ・sequential approach poor responsiveness and throughput ・thread per task approach poor resource management •(6-2) Executor Framework –実行ポリシー –スレッドプール –Executorのライフサイクル –タスクの遅延開始と周期的実行 ・(6-3)並列化できる箇所/すべき箇所を見つける –ページレンダラの例 –Callable and Future –異質なタスクを並列化する限界 –CompletionService –タスクに時間制限を設ける
  • 4. 6-1-1 タスクを逐次的に実行する •List 6-1 Sequential Web Server. –1度に1つのリクエストしか扱えない –サーバが1つのリクエストを処理する間、新たな接続は待たされる –スループットも応答性も得られない –GUIフレームワークでは、single thread で sequentially なタスクに利点 (9章) class SingleThreadWebServer { public static void main(String[] args) throws IOException { ServerSocket socket = new ServerSocket(80); while (true) { Socket connection = socket.accept(); handleRequest(connection); }
  • 5. 6-1-2. タスクのためのスレッドを明示的に作る •List 6-2 Web Server that Starts a New Thread for Each Request. –main loop が クライアントからの接続毎に new thread を作る •new thread がリクエストを処理 •main thread はリクエストを処理しない class ThreadPerTaskWebServer { public static void main(String[] args) throws IOException { ServerSocket socket = new ServerSocket(80); while (true) { final Socket connection = socket.accept(); Runnable task = new Runnable() { public void run() { handleRequest(connection); } }; new Thread(task).start(); }
  • 7. 6-1-3 制限のないスレッド作成の欠点 •スレッドのライフサイクルのオーバヘッド –スレッドの作成と廃棄はただではない •リソースの消費 –特にメモリ。 –実行状態のスレッドの数がプロセッサの数より多いと、スレッドはidle状態 になる –idle状態のスレッドが多いと、メモリ消費量が増えて、garbage collector の負担が大きくなる –全てのCPUがbusyなら、それ以上スレッドを作るのは意味が無いし、サ ーバの能力が劣化する •安定性 –作れるスレッドの数にはlimitがある –limit に達した場合、結果多くの場合、OutOfMemoryError –OOMEからのリカバリはとてもrisky 。このlimitに達しないようなプログラ ムを構造する方がずっと容易。 この方式の問題点:スレッドを作る数に制限が無いこと
  • 8. 6-2 Executor フレームワーク •5章(5-3 p102)で サイズ制限のあるキュー (bounded queues) を使用した –過負荷でメモリ不足にならないために –Thread pool はそれと同じ考え方でスレッドを管理する •java.util.concurrent パッケージは、Executor フレームワークの 一部として、柔軟なThread poolの実装を提供
  • 9. 6-2 Executor フレームワーク •List 6-3 Executor Interface. •taskの submission(依頼) と execution(実行) を分離 –producer consumer pattern (5-3) public interface Executor { void execute(Runnable command); }
  • 10. 6-2-1Executor を使ったWebサーバ • List 6-4. Web Server Using a Thread Pool. class TaskExecutionWebServer { private static final int NTHREADS = 100; private static final Executor exec = Executors.newFixedThreadPool(NTHREADS); public static void main(String[] args) throws IOException { ServerSocket socket = new ServerSocket(80); while (true) { final Socket connection = socket.accept(); Runnable task = new Runnable() { public void run() { handleRequest(connection); } }; exec.execute(task); }
  • 11. 6-2-1Executor を使ったWebサーバ •thread per task approach の動作にするExecutorの例 –List 6-5 •sequential approach の動作にするExecutorの例 –List 6-6 public class ThreadPerTaskExecutor implements Executor { public void execute(Runnable r) { new Thread(r).start(); }; } public class WithinThreadExecutor implements Executor { public void execute(Runnable r) { r.run(); }; }
  • 12. 6-2-2 実行ポリシー •タスクの実行の what, where, when, how –タスクを •どのスレッドで実行するか? •どんな順序で実行するか?(FIFO, LIFO, priority order) •並行に実行する数は? •キューに並ばす最大数は? •過負荷でrejectしなければいけない時にどのタスクを犠牲にする か? アプリケーションの通知はどうするか? •実行前後でどんなアクションを行うべきか? •実行ポリシーの仕様を、タスクの依頼から分離 –実働機 のリソース状況にマッチした実行ポリシーを指定することが困難ではなく なる。
  • 13. 6-2-3 スレッドプール •worker threads の均質な(一種類のスレッドから成る) プールを管理 •利点 –既存の thread の再利用 –thread 作成の待ち時間がタスクの実行を遅らせない→ 応答性向上 –thread pool サイズの適切な tuning •→ プロセッサを無駄に遊ばせない •→ メモリ不足にならない
  • 14. 6-2-3 スレッドプール Executors の static ファクトリメソッド •newFixedThreadPool •固定サイズ •スレッドを最大プールサイズまで作る •プールサイズを一定に保つ •return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()) •newCachedThreadPool •more flexibility •idle threads は刈り取る •要求が増えたら new threads を追加 •プールのサイズ制限が無い •return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
  • 15. 6-2-3 スレッドプール Executors の static ファクトリメソッド •newSingleThreadExecutor •single worker thread •task queue の順序(FIFO, LIFO, priority order)に応じて、 sequentially に処理す る事が保証されている。 •return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); •newScheduledThreadPool •固定サイズ •遅延開始、周期的実行をサポート •Timerに似ている (→ 6-2-5 参照) •return new ScheduledThreadPoolExecutor(corePoolSize); →ThreadPoolExecutorの構成については第8章参照
  • 16. 6-2-4 Executor のライフサイクル •graceful shutdown •開始した仕事は無事に終わるが、新たな仕事は受け付けない •abrupt shutdown •唐突なshutdown。電源OFF。 •ExecutorService •Executor を extends した interface •ライフサイクルを管理するメソッドが定義されている •List 6-7. Lifecycle Methods in ExecutorService. public interface ExecutorService extends Executor { void shutdown(); List<Runnable> shutdownNow(); boolean isShutdown(); boolean isTerminated(); boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
  • 17. 6-2-4 Executor のライフサイクル ExecutorService が暗黙的に定義しているライフサイクル •ExecutorService の ライフサイクルの3つのステート •running •shutting down •terminated •shutdown メソッド •graceful shutdown •新たなタスクは受け付けない •前に依頼されたタスク(実行を開始していないものも含めて)は完了可能 → 終了するまで待つ •shutdownNow メソッド •abrupt shutdown •実行中のタスクのキャンセルを試みる •キューにあってまだ始まっていないタスクはスタートしない
  • 18. 6-2-4 Executor のライフサイクル •シャットダウンの後に依頼されたタスク •rejected execution handler (8-3-3)が取り扱う •黙ってタスクを捨てる or •RejectedExecutionException をthrowする •全てのタスクが完了したら → ExecutorService は terminated ステートに遷移 •awaitTermination メソッド •ExecutorService が terminated ステートへ到達するのを待つ •(apiリファレンス) 「シャットダウン要求後にすべてのタスクが実行を完了していたか、タイムアウトが発生するか、現 在のスレッドで割り込みが発生するか、そのいずれかが最初に発生するまでブロックします」 •isTerminated メソッド •ExecutorService が terminated ステートへ到達したか否かをチェック •(apiリファレンス)「シャットダウンに続いてすべてのタスクが完了していた場合、true を返します。 」 •shutdown に続いて awaitTermination を呼ぶのはよく行われる → シャットダウンに関しては第7章で
  • 19. 6-2-4 Executor のライフサイクル •ライフサイクルのサポートを加えた Webサーバの例 •List 6-8. Web Server with Shutdown Support. class LifecycleWebServer { private final ExecutorService exec = ...; public void start() throws IOException { ServerSocket socket = new ServerSocket(80); while (!exec.isShutdown()) { try { final Socket conn = socket.accept(); exec.execute(new Runnable() { public void run() { handleRequest(conn); }); } catch (RejectedExecutionException e) { if (!exec.isShutdown()) log("task submission rejected", e); } } } public void stop() { exec.shutdown(); } void handleRequest(Socket connection) { Request req = readRequest(connection); if (isShutdownRequest(req)) stop(); else dispatchRequest(req); }
  • 20. 6-2-5 タスクの遅延開始と周期的実行 •(×)java.util.Timer にはいくつか欠点がある → (○)ScheduledThreadPoolExecutor を代わりに使うように。 •Timerの欠点 •1つのTimerで1つのスレッド。 •複数のTimerTaskを実行する時 → 1つの TimerTask の実行が長すぎると → 他のタスクが不正確になる ※ ScheduledThreadPoolExecutor は複数スレッドを使って回避する •システムクロックの変更に影響を受けてしまう •TimerTask が非チェック例外をthrowした時の対応がお粗末 •Timer スレッドがcatchしない •Timer スレッドが終了してしまう。Timer全体がキャンセルされたと誤判断 •"thread leakage" と呼ばれる問題 •(スケジューリング済みで未実行のタスクが実行されない、新たなタスクはスケジュールされない) •→ 7-3 で避ける方法を説明
  • 21. 6-2-5 タスクの遅延開始と周期的実行 •List 6-9 Timer の動作をデモするクラス •DelayQueue •scheduling service を自作する場合に使える •BlockingQueue の実装クラス •ScheduledThreadPoolExecutorのようなスケジューリング機能 public class OutOfTime { public static void main(String[] args) throws Exception { Timer timer = new Timer(); timer.schedule(new ThrowTask(), 1); SECONDS.sleep(1); timer.schedule(new ThrowTask(), 1); // “Timer already cancelled” SECONDS.sleep(5); } static class ThrowTask extends TimerTask { public void run() { throw new RuntimeException(); } }
  • 22. 6-3 並列化できる箇所/すべき箇所を見つける •タスク境界(task boundary)が自明な場合 •server applications の 1つのclient request •1つのclient requestの中にさらに並列できる部分がある (DBサーバなど) •6-3-1 Example: Sequential Page Renderer •一番単純 •長時間待たされる
  • 23. 6-3-2 結果を返すタスク : Callable and Future •Runnable run メソッド •値を返せない •チェックされる例外をthrowできない •Callable call メソッド •値を返せる •チェックされる例外をthrowできる •Executor が実行するタスクのlifecycle •4つの段階: •created •submitted •started •completed •Executor framework でのタスクのキャンセル (※キャンセルについては第7章で) •submit されたけれど、started になってないタスク → いつでもキャンセル可能 •started のタスク → interruption に応答する場合キャンセル可能 •completed のタスク → 何も起きない
  • 24. 6-3-2 結果を返すタスク : Callable and Future •Future •タスクのlifecycleを表現 •前に進めるだけ、後戻りはできない •ExecutorService のlifecycleと同様 •調べるメソッド •タスクが completed かどうか •タスクが cancel されたかどうか •タスクの結果を取り出すメソッド •List 6-11 Callable and Future Interfaces. public interface Callable<V> { V call() throws Exception; } public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionException, CancellationException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, CancellationException, TimeoutException;
  • 25. 6-3-2 結果を返すタスク : Callable and Future •Future.get の振る舞い •タスクのstateによって異なる •completed •直ちにreturnする or •Exceptionをthrow •完了していなければ、完了するまでブロック •Exceptionをthrowして完了の場合 •get は ExecutionException でラップしてrethrowする → getCause メソッドで元の例外を取得できる •cancelled •get は CancellationException を throwする
  • 26. 6-3-2 結果を返すタスク : Callable and Future •Future を作成する方法 •ExecutorService の submit メソッド •FutureTask のインスタンスを明示的に作成 •AbstractExecutorService の newTaskFor メソッド •defaultの実装は FutureTask をnewしてるだけ •overrideする事でFuture のインスタンスの作り方を制御できる(java6から) •List 6-12 Default Implementation of newTaskFor in ThreadPoolExecutor. (参考: クラス図) http://blog.thihy.info/wp-content/uploads/2013/01/%E7%BA%BF%E7%A8%8B%E6%B1%A0%E7%BB%A7%E6%89%BF%E5%85%B3%E7%B3%BB.jpg (参考: FutureTaskクラスがJava SE 6で変更) http://itpro.nikkeibp.co.jp/article/COLUMN/20071001/283396/ protected <T> RunnableFuture<T> newTaskFor(Callable<T> task) { return new FutureTask<T>(task); }
  • 27. 6-3-3 例: Future を使うページレンダラ •2つのタスクに分割 •テキストのrender •画像のダウンロード → Callable を作って → ExecutorService に submit •List 6-13 Waiting for Image Download with Future. public class FutureRenderer { private final ExecutorService executor = ...; void renderPage(CharSequence source) { final List<ImageInfo> imageInfos = scanForImageInfo(source); Callable<List<ImageData>> task = new Callable<List<ImageData>>() { public List<ImageData> call() { List<ImageData> result = new ArrayList<ImageData>(); for (ImageInfo imageInfo : imageInfos) result.add(imageInfo.downloadImage()); return result; } }; Future<List<ImageData>> future = executor.submit(task); renderText(source); try { List<ImageData> imageData = future.get(); for (ImageData data : imageData) renderImage(data); } catch (InterruptedException e) { // thread の interrupted status を再確立する Thread.currentThread().interrupt(); // 結果はいらないので、タスクをcancel future.cancel(true); } catch (ExecutionException e) { throw launderThrowable(e.getCause());
  • 28. 6-3-4 異質なタスクを並列化する限界 前の例(FutureRenderer) では •テキスト処理の時間 < 画像処理の時間 •実行性能は逐次処理とあまり変わらない •しかし、コードは逐次処理よりも複雑に → 苦労の成果にも限界 → 成果が上がるのは同質のタスクの場合
  • 29. 6-3-5 CompletionService: Executor Meets BlockingQueue •複数タスクの完了 •タイムアウトが0の Future.get で何度もポーリング ↓ もっと良い方法 •CompletionService を使う •take や poll メソッドが使える •ExecutorCompletionService •CompletionService の実装クラス
  • 30. 6-3-5 CompletionService: •ExecutorCompletionService のソースの抜粋 public class ExecutorCompletionService<V> implements CompletionService<V> { private final Executor executor; private final AbstractExecutorService aes; private final BlockingQueue<Future<V>> completionQueue; private class QueueingFuture extends FutureTask<Void> { QueueingFuture(RunnableFuture<V> task) { super(task, null); this.task = task; } protected void done() { completionQueue.add(task); } // FutureTask にはtaskが完了したら呼ばれるdoneメソッドがある private final Future<V> task; } … public ExecutorCompletionService(Executor executor) { if (executor == null) throw new NullPointerException(); this.executor = executor; … public Future<V> submit(Callable<V> task) { if (task == null) throw new NullPointerException(); RunnableFuture<V> f = newTaskFor(task); // return new FutureTask<V>(task); // taskの実行をexecutorに委譲 // taskが依頼されるとtaskはQueueingFutureでラップされる executor.execute(new QueueingFuture(f)); return f; } … public Future<V> take() throws InterruptedException { return completionQueue.take(); } public Future<V> poll() { return completionQueue.poll(); }
  • 31. 6-3-6 CompletionService を使ったページレンダラ •List 6-15 •画像1つ1つのダウンロード → 別々のタスク → スレッドプールで実行 public class Renderer { private final ExecutorService executor; Renderer(ExecutorService executor) { this.executor = executor; } void renderPage(CharSequence source) { final List<ImageInfo> info = scanForImageInfo(source); CompletionService<ImageData> completionService = new ExecutorCompletionService<ImageData>(executor); for (final ImageInfo imageInfo : info) completionService.submit(new Callable<ImageData>() { public ImageData call() { return imageInfo.downloadImage(); } }); renderText(source); try { for (int t = 0, n = info.size(); t < n; t++) { // takeで取り出す順番は、タスクが完了した順番 // 何も存在しない場合は待機 Future<ImageData> f = completionService.take(); ImageData imageData = f.get(); renderImage(imageData); } …
  • 32. 6-3-7 タスクに時間制限を設ける •例: 広告配信 •ADサーバから2秒以内に取り込めなかったら •→ デフォルトの広告を表示 •→ 広告の取り込みは中断 •時間指定付きの Future.get •結果が得られば直ちにreturnする •タイムアウトまで得られなければ TimeoutException を throw •get に渡すタイムアウトが負数の場合は0扱い •タスクのキャンセル •TimeoutException を catch → future.cancel(true); → List 6-13, 6-16 で使用 ※第7章
  • 33. 6-3-7 タスクに時間制限を設ける •List 6-16 Fetching an Advertisement with a Time Budget. •広告を取り込むタスク → Executorに依頼 Page renderPageWithAd() throws InterruptedException { long endNanos = System.nanoTime() + TIME_BUDGET; Future<Ad> f = exec.submit(new FetchAdTask()); // ページを Render しながら 広告を待つ Page page = renderPageBody(); Ad ad; try { // 一定の時間(TIME_BUDGET)だけ待つ long timeLeft = endNanos - System.nanoTime(); ad = f.get(timeLeft, NANOSECONDS); } catch (ExecutionException e) { ad = DEFAULT_AD; } catch (TimeoutException e) { ad = DEFAULT_AD; f.cancel(true); } page.setAd(ad); return page;
  • 34. 6-3-8 例: 旅行予約ポータル •例: 複数企業からビッドを取り込む •一定時間内の応答だけ提示 •タスク境界 •1つの起業からのビッド → 独立している → 妥当なタスク境界 •複数企業の数分、タスクをn個 •スレッドプールに依頼 •複数のFutureで管理 •時間指定付きの Future.get •結果を逐次的に取り込む ↓もっと簡単な方法 •時間指定付きの invokeAll
  • 35. 6-3-8 例: 旅行予約ポータル •invokeAll •引数 : a collection of tasks •戻り値: a collection of Futures •この2つのcollectionは構造が同じ。 •引数のタスクのcollectionのiteratorの順番で戻り値のcollectionにFutureが加わる •時間指定付きの invokeAll がリターンするのは •全てのタスクが完了したとき •呼び出しているスレッドがinterruptされた時 •タイムアウトになった時 → 各タスクは •正常に完了している or •キャンセルされている 、かのどちらか。 → get または isCancelled を読んでどちらの結果なのか判別できる
  • 36. 6-3-8 例: 旅行予約ポータル •List 6-17 時間制限付きで旅行の見積もりをリクエストする (抜粋) private class QuoteTask implements Callable<TravelQuote> { … public List<TravelQuote> getRankedTravelQuotes( …) { … List<QuoteTask> tasks = new ArrayList<QuoteTask>(); for (TravelCompany company : companies) tasks.add(new QuoteTask(company, travelInfo)); List<Future<TravelQuote>> futures = exec.invokeAll(tasks, time, unit); List<TravelQuote> quotes = new ArrayList<TravelQuote>(tasks.size()); Iterator<QuoteTask> taskIter = tasks.iterator(); for (Future<TravelQuote> f : futures) { QuoteTask task = taskIter.next(); try { quotes.add(f.get()); } catch (ExecutionException e) { quotes.add(task.getFailureQuote(e.getCause())); } catch (CancellationException e) { quotes.add(task.getTimeoutQuote(e)); } } Collections.sort(quotes, ranking); return quotes;