java.util.concurrent.DelayQueue可以忽略过期的元素

时间:2012-08-27 12:31:34

标签: java multithreading priority-queue

下面的Java代码示例使用java DelayQueue来处理任务。但是,从另一个线程插入任务似乎会破坏(我的)预期行为。

道歉,代码示例很长,但总结如下:

  1. 主线程将5个任务(A-E)添加到具有各种延迟(0ms,10ms,100ms 1000ms,10000ms)的DelayQueue中
  2. 启动另一个步骤,在3000ms
  3. 之后向DelayQueue添加另一个任务
  4. 主线程轮询DelayQueue并在每个任务到期时报告
  5. 8000ms后,主线程报告DelayQueue中剩余的任务
  6. 我从代码示例中获得的输出是:

    ------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
    

    我的问题是:为什么在15000ms之后,DelayQueue中还有剩余的任务(即GetDelay()返回-ve值的地方)?

    我检查过的一些事情:

    • 我已经实现了compareTo()来定义任务的自然顺序
    • equals()与compareTo()
    • 一致
    • hashCode()已被覆盖

    我最感兴趣的是学习如何解决这个问题。提前感谢您的协助。 (以及迄今为止帮助我解决的所有Stack Overflow答案:)

        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) {
          }
        }
        }
    

3 个答案:

答案 0 :(得分:5)

原因是数字溢出。

您的compareTo()方法将long差异(纳秒)与int进行了对比,但在int中无法保存超过2.2秒的纳秒数而且您&# 39; ll得到一个溢出 - 给出或多或少的随机结果,所以如果它在将来超过2.2秒后,队列中的顺序可能落后于一个稍后到期的顺序。

poll()不会超出队列中的下一个项目,当项目放入队列时,compareTo方法的顺序由<{p}}方法定义。


此外,equals()应同意hashCode()以及compareTo()。有关详细信息,请参阅javadoc for hashCode()

答案 1 :(得分:4)

因为您的comapareTo方法充满了缺陷。正确实施如下。一旦你改变如下,你的所有问题都将得到解决。如果或遵守compareTo合同

,请务必重复使用compareTo方法
return Long.valueOf(this.ripe).compareTo(that.ripe);

答案 2 :(得分:2)

除非这是实施事件调度程序的练习,否则最好使用ScheduledExecutorService。它会做你想要做的所有事情,等等。