|
Concurrency Utilities その参 時間表現の巻 |
||||
|
||||
非同期処理やリアルタイム処理を行うとき、よく出てくるのがタイムアウトです。ようするに、5 秒待つけどそれ以上は待てないよみたいなことです。 たとえば、Object#wait(long timeout) メソッドなどです。これは、timeout ミリ秒以内に notify されればメソッドを抜けますが、timeout ミリ秒たったら notify されていなくてもメソッドを抜けてしまいます。 しかし、マルチメディア系のアプリケーションなどリアルタイム性を求められるアプリケーションではミリ秒では不十分です。また、マシンのスペックがあがるにつれ、ミリ秒以下の時間を扱うのも容易になってきています。 一応、J2SE 1.4 でもナノ秒を扱うための Object#wait(long timeout, int nanos) というオーバロードされたメソッドが定義されています。しかしミリ秒とナノ秒を別々に指定しなければいけないなど、使い勝手はいまいちです。 また、Object#wait メソッドや Thread#sleep メソッドでナノ秒は指定できるのですが、ナノ秒単位の時刻の取得はできませんでした。 そこで、登場するのが ConcurrencyUtilities の TimeUnit クラスです。 また、ナノ秒単位の時間を取得することもできるようになっています。ナノ秒はSystem#nanoTimes メソッドを使用して取得します。このメソッドで取得できる値は、System#currentTimeMillis メソッドと違って特に意味はありません。開始時間と終了時間を差をとるような使い方が想定されています。
|
|
||||||||||||||||||
TimeUnit クラスは基本的にユーザはインスタンス化しません。定数として定義されているものを使用します。
たとえば java.util.concurrent.Future#get メソッドなどで使用される方法が TimeUnit クラスの基本的な使い方になります。
基本的には TimeUnit クラスが表す時間単位とその単位で示される値のペアで使います。TimeUnit の Unit は単位なので、あくまでも時間の単位を表すためのクラスだということがお分かりでしょうか。 基本的にはこれだけです。終わり。 というわけにも行かないので、もう少し TimeUnit が備えている機能を見てみましょう。
|
|
||||||||||
さて、1 秒は何ナノ秒でしょう。正解は 1 s = 1,000,000,000 ns です。変換自体はたいしたことありませんが、0 の数を間違えてしまいそうです ^^;; こんなことはソフトでやってしまおうというのが TimeUnit#convert メソッドです。
とりあえずいろんな変換をしてみました。
convert メソッドの第 1 引数は変換したい時間感覚です。そして、第 2 引数がその単位になります。ですから、1番始めは 0 秒をミリ秒に変換しています。 3 番目の例が、10 マイクロ秒をミリ秒に変換しています。出力するとどうなるでしょう。
10 マイクロ秒は 1 ミリ秒に足りないので 0ms になってしまいました ^^;; あくまでも整数で小数を使った表現にはならないことが注意点です。 convert メソッドの簡易版として toSeconds, toMillis, toMicros, toNanos メソッドが用意されています。
引数が先ほどと違って、オブジェクトが表わしている時間単位での値になります。したがって、結果は次のようになります。
実際にはこれらのメソッドは convert メソッドを内部でコールしているだけのメソッドになります。 今までの例だと単位の変換で切捨てなのか、四捨五入なのかよく分かりません。切り上げしていないことは分かりますが。そこで、次のような例を書いてみました。
微妙なところをついたつもりですが ^^;; 実行してみると次のようになりました。
やはり、四捨五入ではなくて切り捨てのようです。
|
|
|||||
Tiger になって Thread#sleep もナノ秒が使えるようになりました。Thread#sleep(long millis, int nanos) メソッドがオーバロードされています。 これを使ってもいいのですが、TimeUnit クラスでもスリープをすることができます。スリープするには TimeUnit#sleep(long timeout) メソッドを 使用します。Thread#sleep メソッドより引数が 1 つだけなので分かりやすいのではないでしょうか。具体的な使い方をみればもう少し利点が分かりやすいと思います。
単にスリープするだけのサンプルです。static インポートを使用しているので、クラス名を書かなくても SECONDS などと書くことができます。
SECONDS.sleep(1L) は 1 秒のスリープ。MILLISECONDS.sleep(100L) は 100 ミリ秒のスリープということになります。分かりやすい表記だと思いませんか。 実際には Thread#sleep メソッドをコールしているだけなのですが、より使いやすくしたというだけです。
|
|
|||||
スリープができるのですから、wait もできるのです。スリープと同じように Object#wait メソッドは Object#wait(long timeout, int nanos) がオーバロードされています。TimeUnit クラスを使用する場合は、TimeUnit#timedWait(Object object, long timeout) メソッドを 使用します。
1,000 ミリ秒のタイムアウトでタイムアウトを行うサンプルです。
Object#wait メソッドと使い方は変わりません。使いやすくなっただけです。 同様に TimeUnit クラスには timedJoin メソッドもあります。でも、それほど使用頻度は高くないと思うので、ここでは省略。
|
|
||||
この解説では TimeUnit クラスを単体で使うことに的を絞って説明しましたが、実際には他のクラスと一緒に使うことの方が多そうです。たとえば
など java.util.concurrent パッケージでは大活躍です。大体の場合、タイムアウトの設定に使われるようです。
今回使用したサンプルはここからダウンロードできます。 参考
(Mar. 2004) |
|