2013年5月21日火曜日

[Java]一定時間経過してから逐次処理を実行する(Queue)

処理をキューイングして、逐次処理をする。よくあるパターンです。一般的にこういう処理を実装するのにはQueue<E>を使うと思います。
では、キューに入れるけど、一定時間経過してからしか処理したい。または、連打された場合、最後の処理だけ実行したい。みたいなことがあると思います。
こういう時に、BlockingQueue<E>を使います。BlockingQueueはインタフェースです。そのため、その実装であるjava.util.concurrent.DelayQueue<E>を使います。

でも、addできるのは、java.util.concurrent.Delayedをインプリメントしたクラスだけ。

実装した例は以下のような感じ。


    public static void main(String[] args) throws InterruptedException {

        long[] waits = { 1086012 };
        String[] tests = { "10""8""6""0""1""2" };

        // 遅延時間のばらばらのデータをqueueに入れる
        BlockingQueue<Task> queue = new DelayQueue<Task>();
        for (int i = 0; i < waits.length; i++) {
            queue.add(new Task(tests[i], waits[i]));
        }

        // 空になるまでループする。
        while (!queue.isEmpty()) {
            Task task = queue.take();
            task.execute();
        }

    }

    /**
     * java.util.concurrent.Delayedを実装した例.
     * 
     */

    static class Task implements Delayed {

        final private long execTime;
        final private String text;

        /**
         * テキストと遅延時間を指定したコンストラクタ.
         * 
         * @param text
         * @param wait
         */

        Task(String text, long wait) {
            this.text = text;
            this.execTime = System.nanoTime() + TimeUnit.SECONDS.toNanos(wait);
        }

        /*
         * (non-Javadoc)
         * 
         * @see java.lang.Comparable#compareTo(java.lang.Object)
         */

        @Override
        public int compareTo(Delayed o) {
            long diff = execTime - ((Task) o).execTime;
            if (diff == 0) {
                return 0;
            }

            return (diff > 0) ? 1 : -1;
        }

        /*
         * (non-Javadoc)
         * 
         * @see
         * java.util.concurrent.Delayed#getDelay(java.util.concurrent.TimeUnit)
         */

        @Override
        public long getDelay(TimeUnit unit) {
            // 残遅延時間を返却
            System.out.println(String.format("%tT getDelay text=[%s] w=[%d]",
                    new Date(), text, execTime - System.nanoTime()));
            return execTime - System.nanoTime();
        }

        /**
         * 処理.
         */

        public void execute() {
            System.out.println(String.format("%tT execute  text=[%s]秒後",
                    new Date(), text));
        }

    }

■実行結果
00:51:24 getDelay text=[0] w=[-910031]
00:51:24 execute text=[0]秒後
00:51:24 getDelay text=[1] w=[892204037]
00:51:25 getDelay text=[1] w=[-1095530]
00:51:25 execute text=[1]秒後
00:51:25 getDelay text=[2] w=[998366343]
00:51:26 getDelay text=[2] w=[-1155035]
00:51:26 execute text=[2]秒後
00:51:26 getDelay text=[6] w=[3995886907]
00:51:30 getDelay text=[6] w=[-13674573]
00:51:30 execute text=[6]秒後
00:51:30 getDelay text=[8] w=[1985765928]
00:51:32 getDelay text=[8] w=[-11499437]
00:51:32 execute text=[8]秒後
00:51:32 getDelay text=[10] w=[1986226602]
00:51:34 getDelay text=[10] w=[-11083182]
00:51:34 execute text=[10]秒後

Queueに登録する順は関係ありませんが、キューの処理順は、compareTo()メソッドの戻り順で処理されます。そのため、遅延時間によって処理順を決めています。 「execute」とログが出ている行を追っかけていくと、指定した通りの時間で処理されていますね。 ログをそこだけに絞り込むと、以下のようになります。遅延指定した時間に実行されています。
00:51:24 execute text=[0]秒後
00:51:25 execute text=[1]秒後
00:51:26 execute text=[2]秒後
00:51:30 execute text=[6]秒後
00:51:32 execute text=[8]秒後
00:51:34 execute text=[10]秒後

0 件のコメント: