使用Java的DelayQueue容易碰到的一个坑

先看一下整个code吧:

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



public class DelayQueueExample {

/**
 * @param args
*/
 public static void main(String[] args) {
 // TODO Auto-generated method stub
 DelayQueue dq=new DelayQueue();
 long now = System.currentTimeMillis();
 System.out.println("current time in ms:"+ now);
 DelayedElement ob1=new DelayedElement("e1", now + 1000);
 DelayedElement ob2=new DelayedElement("e2", now + 5000);
 DelayedElement ob3=new DelayedElement("e3", now + 1500);

dq.add(ob1);
dq.add(ob2);
dq.add(ob3);

 try {
Thread.sleep(1);
 } catch (InterruptedException e) { 
 throw new RuntimeException( e );
}


 while(dq.size() > 0){
 try {
 DelayedElement e = dq.take(); 
 System.out.println("current time in ms:"+ System.currentTimeMillis() +", element:"+ e.name);

 } catch (InterruptedException e) { 
 throw new RuntimeException( e );
}

}
}

 static class DelayedElement implements Delayed {
 public long time;
 public String name;
 public DelayedElement(String name, long time){
 this.name = name;
 this.time = time;
}
@Override
 public int compareTo(Delayed o) {
 // TODO Auto-generated method stub
 if(this.time < ((DelayedElement)o).time) return -1;
 else if(this.time > ((DelayedElement)o).time)return 1;
 else return 0;
}

@Override
 public long getDelay(TimeUnit unit) {
 // TODO Auto-generated method stub
 long r = unit.convert(time - System.currentTimeMillis(), TimeUnit.NANOSECONDS);
 //System.out.println("delay:"+ r);
 return r;
}

}
}

注意getDelay(TimeUnit unit),这个是实现Delayed这个接口时候必须要实现的一个方法,容易遇坑的地方就是
long r = unit.convert(time - System.currentTimeMillis(), TimeUnit.NANOSECONDS);
这里应该使用的TimeUnit.MILLISECONDS而不是TimeUnit.NANOSECONDS,但是你会发现不管你用哪一个(或者其他的TimeUnit),这个程序的正确性是ok的,都会delay你所要的时间,例如分别使用这两种TimeUnit的输出: If using MILLISECONDS: current time in ms: 1369644922697 current time in ms: 1369644923697, element:e1 current time in ms: 1369644924197, element:e3 current time in ms: 1369644927697, element:e2 If using NANOSECONDS: current time in ms: 1369645748910 current time in ms: 1369645749910, element:e1 current time in ms: 1369645750410, element:e3 current time in ms: 1369645753910, element:e2 那么会有什么问题呢?
 public E take() throws InterruptedException {
       final ReentrantLock lock = this.lock;
       lock.lockInterruptibly();
    try {
      for (;;) {
       E first = q.peek();
       if (first == null) {
         available.await();
       } else {
    long delay = first.getDelay(TimeUnit.NANOSECONDS);
   if (delay > 0) {
  long tl = available.awaitNanos(delay);
  } else {
  E x = q.poll();
  assert x != null;
  if (q.size() != 0)
  available.signalAll(); // wake up other takers
  return x;
 
  }
  }
  }
  } finally {
 lock.unlock();
 }
}

看一下DelayQueue的take()方法会发现,在队列首对象的delay>0的时候,等待的时间单位是nanos(193行),所以如果我刚才getDelay()里面没有将ms转换成ns,那么数值会小很多,line 193 会很快执行,再次循环进行判断,delay仍然大于0,注意,总的等待时间是固定的,现在是每次wait的时间片变小了,所以循环的次数多了,造成一个结果就是cpu占用上升! 如果打印出每次delay的值便可以看到用nanos多做了多少次循环,读者可以自己看一下

你可能感兴趣的:(多线程)