2012年12月19日水曜日

HTML5仕様策定完了、HTML 5.1に向けた取り組みも開始

 2012年12月17日、World Wide Webコンソーシアム(W3C)HTML5およびCanvas 2Dに関する仕様策定を完了したことを発表した。現段階ではW3C標準仕様ではないが、機能的に完成しているため、間もなくW3C標準仕様になることが予想される。さらに同日、標準化の次を視野に入れたHTML 5.1およびCanvas 2D, Level 2の第1草案を発表。既存のHTML機能に加え、ビルトインHTML5アクセシビリティ、レスポンシブイメージ、およびアダプティブストリーミングを補完するための拡張機能など、新たな機能の開発を継続して強化していくという。

 今回のリリースにあたり、W3C CEOジェフ・ヤッフェ(Jeff Jaffe)氏は「ステークホルダーは、ウェブテクノロジーの広まりにともない、さらに安定した標準を望むようになっている。現今、企業は、今後HTML5によって何ができるか、そして、今後顧客がどのようなことを要望してくるのかを分かっている。同じように、ディベロッパーも、スマートフォン、車、テレビ、電子書籍、電子署名、あるいは、未知のデバイスなどと連携するためには、どのようなスキルを育成すべきなのか理解している」と述べている。

 HTML5の開発ツールを提供しているKendo UIが行った調査によると、63%を超えるディベロッパーが「HTML5を使って積極的に開発している」という旨を示している。W3C HTML Working Groupは、現在のHTML5の実装を分析し、コミュニティと連携しながらテスト開発に取り組み、2014年中旬にはこの実装フェイズを終了するとし、ロイヤリティフリーとして公開する予定だという。

2012年12月17日月曜日

ThreadPool

 Java5以降、ExecutorServiceを用いることでスレッドプールをサクッと作ることができるようになりました。例えば、最大同時実行数が10となるようなスレッドプールの場合、

ExecutorService threadPool =

Executors.newFixedThreadPool(10);

でスレッドプールを準備し、あとはthreadPool.execute(Thread)って感じで並行処理させたいスレッドをぽいぽい投げ込めばOK。
 Java1.4の時に自前でスレッドプール(のようなもの)を実装したことがあるのですが、それを思うとすごく簡単ですね。
 で、並行処理とよく組み合わせることになりそう(僕だけ?)なのが、「全てのスレッドの処理が終わるまで待機」ってやつでしょうか。これも、Java5なら

threadPool.shutdown();

threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);

でできあがり。shutdown()を忘れると、awaitTermination以降が進まなくなります。最初それに気付かず、「終わらない」プログラムを作ってしまいました。
 ちなみに、JavaDoc(日本語版)には、
シャットダウン要求後にすべてのタスクが実行を完了する、タイムアウトが発生する、現在のスレッドで割り込みが発生する、のどれかが最初に起きるまでブロックします。
と記載されています。最初に気付きましょうね。
#awaitTerminationでLong.MAX_VALUEをセットしているのは、タイムアウトを実質無効としたいためです。

ExecutorServiceの正しい終了(shutdown)の仕方

ExecutorServiceを使うと簡単にマルチスレッド処理が行えますが、
終了もshutdownメソッドを呼ぶだけと思っていませんか?
shutdownメソッドだけだと不十分です。

学校の先生と生徒の関係で説明します。

shutdownメソッドは指示するだけ
学校で先生が生徒に対して、「問題が解けたら教えてねー」と言います。
生徒は問題が解けた人から「先生できたー!!」と言ってきます。
全員問題が解けた段階で授業が終わるとしましょう。

対応はこう。
先生: ExecutorService
生徒: ExecutorService内のスレッド
問題: ExecutorService.executeに渡すタスク(Runnable)
授業: mainスレッド
学校: Javaプロセス

「問題が解けたら教えてねー」がshutdownメソッド。
「先生できたー!!」が各タスクの完了です。
全部のタスクが完了したらmainスレッド(授業)が終了します。

ここで、とても難しい時間のかかる問題があるとします。当然生徒は問題を解くのにとても時間がかかります。
でも先生(shutdownメソッド)は何もしません。「先生できたー!!」って言ってくるまで「かたくな」に待ちますw
shutdownメソッドは指示するだけだからです。
授業は定時に終わってしまっても(mainスレッドが終わっても)、
学校を閉める事(Javaプロセスを終了する事)はできません。まだ生徒がいるのでww

あなたが先生だとどうしますか?
一定時間経った所で、解き方を教えたり宿題にして授業を終わらせますよね!

時間を決めてタスク完了を待つ
ある一定時間待ってもタスクが終わらない場合、
タスクを中断させることで、ずっとタスク完了を待つことをしないですみます。

awaitTerminationメソッドがこの役割を果たします。

使い方としては、shutdownメソッド実行後にawaitTerminationを実行します。
awaitTerminationの第一引数の時間だけ同期して待ちます。
指定した時間内に全てのタスクが完了した場合、trueを返します。
指定した時間経っても全てのタスクが完了しない場合、falseを返します。

falseを返されるというのは、授業の時間が終わっても、
問題が解けていない生徒がいる状態です。

「解けてない人宿題で明日提出しなさい!!」と言って授業を終わらせましょう!
shutdownNowメソッドがやってくれます。

shutdownNowメソッドは、実行中の全てのスレッドに対してinterruptedを発行して
スレッドを中断します。
そのため、各スレッドではInterruptedExceptionが発生します。(発生するメソッドを使っている場合)

正しい終了の仕方は以下になります。

public static void main(String[] args) {

ExecutorService pool = Executors.newFixedThreadPool(5);
final long waitTime = 8 * 1000;
final long awaitTime = 5 * 1000;

Runnable task1 = new Runnable(){
public void run(){
try {
System.out.println("task1 start");
Thread.sleep(waitTime);
System.out.println("task1 end");
} catch (InterruptedException e) {
System.out.println("task1 interrupted: " + e);
}
}
};

Runnable task2 = new Runnable(){
public void run(){
try {
System.out.println(" task2 start");
Thread.sleep(1000);
System.out.println(" task2 end");
} catch (InterruptedException e) {
System.out.println("task2 interrupted: " + e);
}
}
};
// ある生徒に難しい問題を解かせる
pool.execute(task1);

// 生徒にたくさん問題を解かせる
for(int i=0; i<1000; ++i){
pool.execute(task2);
}

try {
// 「問題が解けたら教えてねー」と生徒に伝える
pool.shutdown();

// 「○○分までにできなかったら全部宿題ねー」と生徒に言って待つ
// (全てのタスクが終了した場合、trueを返してくれる)
if(!pool.awaitTermination(awaitTime, TimeUnit.MILLISECONDS)){
// タイムアウトした場合、全てのスレッドを中断(interrupted)してスレッドプールを破棄する。
pool.shutdownNow();
}
} catch (InterruptedException e) {
// awaitTerminationスレッドがinterruptedした場合も、全てのスレッドを中断する
System.out.println("awaitTermination interrupted: " + e);
pool.shutdownNow();
}

System.out.println("end");
}
waitTimeがawaitTimeより大きい場合、タイムアウトして実行中のタスクが
interruptedすることがわかると思います。
逆にwaitTimeを小さくし、awaitTimeを大きく取ると、綺麗に終了すると思います。

ここで、awaitTimeとshutdownNowメソッドを全てコメントアウトして
shutdownメソッドのみにした場合どうなるか試してみてください。

mainメソッド終了の「end」が出力された後にたくさんtask2のstartとendが
表示されると思います。
これが授業が終わっていても学校を閉じる事ができない状態です。
最悪の場合、Javaプロセスがずっと残ってしまいます。

awaitTerminationとshutdownNowは必ず書くようにしましょう。

shutdownメソッドは重要
awaitTerminationを行なっていればshutdownNowだけでいいような気がしますが違います。
shutdownメソッドは、「それ以上はやらなくていいよ」とも言ってくれます。

shutdownメソッド実行後は、タスクを追加を禁止してくれます。
shutdownメソッド実行後にexecuteメソッドを実行するとRejectedExecutionExceptionが発生します。(submitメソッドなども)

またawaitTerminationは、awaitTerminationメソッド実行時点のタスク完了を待つのではなく、
awaitTerminationメソッド実行後、タスクが全く動いていない状態を監視します。
そのため、shutdownメソッドを実行せずにawaitTerminationを実行しても、
追加タスクによって「タスクが全く動いていない状態」にならない場合、
期待しないawaitTerminationのタイムアウトが発生するかもしれません。

そのため、実行順序としては、

shutdownメソッド
awaitTerminationメソッド
shutdownNowメソッド(例外発生時、タイムアウト時のみ)
となります。

システムによっては「強制的に停止してはいけない」場合もあると思うので、必ずではないですが、基本的にこの順番で実行することをお勧めします。

なお、一定間隔でタスクのスケジューリングができるScheduledExecutorServiceも
同様の順番で終了できますが、 scheduledメソッドを利用した場合はもう少し手順が必要です。