第45回 BlockingQueueインターフェース
今回のテーマは「BlockingQueue」です。
QueueインターフェースのサブインターフェースにBlockingQueueインターフェースがあります。 BlockingQueueインターフェースを実装するクラスにはLinkedBlockingQueue、ArrayBlockingQueue、PriorityBlokingQueue、DelayQueueなどがあります。
BlockingQueue は容量が制限される場合があります。 LinkedBlockingQueue、ArrayBlockingQueueは容量制限を設定できます。 要領制限のあるBlockingQueueに要素を追加する場合はofferメソッドの使用が推奨されています。 addメソッドやaddAllメソッドでも要素を追加する事ができますが容量制限を超えてしまった場合はIllegalStateException がスローされてしまいます。
BlockingQueueはマルチスレッドでの使用が想定されています。 putメソッドはキューに要素を追加しますが、必要に応じ、空間が利用可能になるまで待機します。 takeメソッドはキューから要素を取得および削除しますが、要素が存在しない場合は待機します。
次のサンプルコードは容量制限があるキューを用意し、要素を追加するスレッドと要素を取得するスレッドを起動し、それぞれ100回繰り返すプログラムです。(J2SE5.0以上対応)
import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; public class Main { public static void main(String[] args) throws Exception { final BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<String>(1); // キューに要素を追加するスレッド new Thread(new Runnable() { public void run() { for (int i = 0; i < loopTime; i++) { try { blockingQueue.put("PUT"); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); // キューから要素を取得するスレッド new Thread(new Runnable() { public void run() { for (int i = 0; i < loopTime; i++) { try { blockingQueue.take(); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); }
}
キューは要素を1個しか保持できないので、実行すると必ず要素の追加と取得が交互に繰り返されます。 容量制限があるBlockingQueueを使用することでスレッドの実行順が制御されています。
PriorityBlockingQueue、DelayQueueはFIFOではありません。 PriorityBlockingQueueはPriorityQueue同様、要素の取得順を指定できるBlockingQueueです。 DelayQueueはDelayed要素を格納するBlockingQueueです。 Delayedインターフェースを実装するクラスはgetDelayメソッドとcompareToメソッドを実装する必要があります。 DelayQueueは遅延時間の経過後にのみ要素を取得できます。 具体的にはgetDelayメソッドが0以下の数値を返す場合のみ取得できます。 また、要素の取得順はcompareToメソッドによって決定されます。
次のサンプルコードは要素取得時に時間を出力するプログラムです。(J2SE5.0以上対応)
import java.util.Date; import java.util.concurrent.BlockingQueue; import java.util.concurrent.DelayQueue; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; public class Main { public static void main(String[] args) throws Exception { BlockingQueue<IDelayed> blockingQueue = new DelayQueue<IDelayed>(); blockingQueue.offer(new IDelayed(20)); blockingQueue.offer(new IDelayed(10)); while (!blockingQueue.isEmpty()) { System.out.println(blockingQueue.take().getDate()); } } } class IDelayed implements Delayed { private Long takeTimeMillis; public IDelayed(long delayTimeSeconds) { long delayTimeMillis = TimeUnit.SECONDS.toMillis(delayTimeSeconds); takeTimeMillis = delayTimeMillis + System.currentTimeMillis(); } public long getDelay(TimeUnit timeUnit) { return takeTimeMillis - System.currentTimeMillis(); } public int compareTo(Delayed delayed) { IDelayed iDelayed = (IDelayed) delayed; return takeTimeMillis.compareTo(iDelayed.takeTimeMillis); } public Date getDate() { return new Date(); } }
IDelayedインスタンスの生成時に遅延時間を秒単位で指定し、取得可能時間を保持します。
getDelayメソッドは取得可能時間を越えた場合に0以下の数値を返すようになっています。
compareToメソッドは取得可能時間が小さい要素から取得できるように実装しました。
実行すると約10秒経過後と約20秒経過後に時間が表示されます。