6

以下の Java コード サンプルでは、​​Java DelayQueue を使用してタスクを処理します。ただし、別のスレッドからのタスクの挿入は、(私の) 予想される動作を混乱させるようです。

コード例が非常に長いことをお詫びしますが、要約すると:

  1. メイン スレッドは、さまざまな遅延 (0ms、10ms、100ms、1000ms、10000ms) を持つ 5 つのタスク (AE) を DelayQueue に追加します。
  2. 別のトレッドが開始され、3000 ミリ秒後に別のタスクが DelayQueue に追加されます
  3. メイン スレッドは DelayQueue をポーリングし、各タスクの期限が切れると報告します。
  4. 8000 ミリ秒後、メイン スレッドは DelayQueue に残っているタスクを報告します。

コードサンプルから得られる出力は次のとおりです。

------initial tasks ---------------
task A due in 0ms
task B due in 9ms
task C due in 99ms
task D due in 999ms
task E due in 9999ms
task F due in 99999ms
------processing--------------------
time = 5    task A due in -1ms
time = 14   task B due in 0ms
time = 104  task C due in 0ms
time = 1004 task D due in 0ms
time = 3003 added task Z due in 0ms
------remaining after 15007ms -----------
task F due in 84996ms
task E due in -5003ms
task Z due in -12004ms

私の質問は: なぜ 15000 ミリ秒後に期限切れのタスクが DelayQueue に残っているのですか (つまり、GetDelay() が -ve 値を返す場所)?

私がチェックしたいくつかのこと:

  • タスクの自然な順序を定義するために compareTo() を実装しました
  • equals() は compareTo() と一致しています
  • hashCode() はオーバーライドされました

この問題を解決する方法を学ぶことに最も興味があります。よろしくお願いいたします。(そして、これまでに私を助けてくれたすべてのスタックオーバーフローの回答について:)

    package test;

    import java.util.concurrent.DelayQueue;
    import java.util.concurrent.Delayed;
    import java.util.concurrent.TimeUnit;

    public class Test10_DelayQueue {

       private static final TimeUnit delayUnit = TimeUnit.MILLISECONDS;
       private static final TimeUnit ripeUnit = TimeUnit.NANOSECONDS;

       static long startTime;

       static class Task implements Delayed {    
          public long ripe;
          public String name;    
          public Task(String name, int delay) {
             this.name = name;
             ripe = System.nanoTime() + ripeUnit.convert(delay, delayUnit);
          }

      @Override
      public boolean equals(Object obj) {
         if (obj instanceof Task) {
            return compareTo((Task) obj) == 0;
         }
         return false;
      }

      @Override
      public int hashCode() {
         int hash = 7;
         hash = 67 * hash + (int) (this.ripe ^ (this.ripe >>> 32));
         hash = 67 * hash + (this.name != null ? this.name.hashCode() : 0);
         return hash;
      }

      @Override
      public int compareTo(Delayed delayed) {
         if (delayed instanceof Task) {
            Task that = (Task) delayed;
            return (int) (this.ripe - that.ripe);
         }
         throw new UnsupportedOperationException();
      }

      @Override
      public long getDelay(TimeUnit unit) {
         return unit.convert(ripe - System.nanoTime(), ripeUnit);
      }

      @Override
      public String toString() {
         return "task " + name + " due in " + String.valueOf(getDelay(delayUnit) + "ms");
          }
       }

       static class TaskAdder implements Runnable {

      DelayQueue dq;
      int delay;

      public TaskAdder(DelayQueue dq, int delay) {
         this.dq = dq;
         this.delay = delay;
      }

      @Override
      public void run() {
         try {
            Thread.sleep(delay);

            Task z = new Task("Z", 0);
            dq.add(z);

            Long elapsed = System.currentTimeMillis() - startTime;

            System.out.println("time = " + elapsed + "\tadded " + z);

         } catch (InterruptedException e) {
         }
      }
    }

    public static void main(String[] args) {
      startTime = System.currentTimeMillis();
      DelayQueue<Task> taskQ = new DelayQueue<Task>();

      Thread thread = new Thread(new TaskAdder(taskQ, 3000));
      thread.start();

      taskQ.add(new Task("A", 0));
      taskQ.add(new Task("B", 10));
      taskQ.add(new Task("C", 100));
      taskQ.add(new Task("D", 1000));
      taskQ.add(new Task("E", 10000));
      taskQ.add(new Task("F", 100000));

      System.out.println("------initial tasks ---------------");
      Task[] tasks = taskQ.toArray(new Task[0]);
      for (int i = 0; i < tasks.length; i++) {
         System.out.println(tasks[i]);
      }

      System.out.println("------processing--------------------");
      try {
         Long elapsed = System.currentTimeMillis() - startTime;
         while (elapsed < 15000) {
            Task task = taskQ.poll(1, TimeUnit.SECONDS);
            elapsed = System.currentTimeMillis() - startTime;
            if (task != null) {
               System.out.println("time = " + elapsed + "\t" + task);
            }
         }

         System.out.println("------remaining after " + elapsed + "ms -----------");
         tasks = taskQ.toArray(new Task[0]);
         for (int i = 0; i < tasks.length; i++) {
            System.out.println(tasks[i]);
         }

      } catch (InterruptedException e) {
      }
    }
    }
4

3 に答える 3

5

あなたのcomapareTo方法は欠陥だらけだからです。正しい実装は次のとおりです。以下のように変更すると、すべての問題が解決されます。契約compareToに準拠している場合は、常にメソッドの再利用を試みますcompareTo

return Long.valueOf(this.ripe).compareTo(that.ripe);
于 2012-08-27T12:49:48.523 に答える
5

理由は、数値オーバーフローによるものです。

メソッドはナノ秒単位の差を にcompareTo()キャストしていますが、2.2 秒以上のナノ秒を に保持することはできず、オーバーフローが発生します - 多かれ少なかれランダムな結果が得られるため、キュー内の順序が 1 つ遅れている可能性があります2.2 秒以上先に有効期限が切れる場合は、有効期限が遅くなります。longintint

poll()キュー内の次のアイテムを超えて検索しません。その順序はcompareTo、アイテムがキューに配置されるときにメソッドによって定義されます。


また、 、およびequals()に同意する必要があります。詳細については、 javadoc を参照してください。hashCode()compareTo()hashCode()

于 2012-08-27T13:43:24.977 に答える
3

これがイベントスケジューラを実装するための演習でない限り、を使用することをお勧めしますScheduledExecutorService。それはあなたがやろうとしているすべてのことなどを行います。

于 2013-02-27T01:24:31.617 に答える