打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
使用Java的DelayQueue容易碰到的一个坑
     今天不忙,学习一下java.util.concurrent.DelayQueue这个类的使用。参照了
http://www.concretepage.com/java/example_delayqueue_java.php
上的例子,但是这里有个坑。

先看一下整个code吧:

Java代码  
  1. import java.util.concurrent.DelayQueue;  
  2. import java.util.concurrent.Delayed;  
  3. import java.util.concurrent.TimeUnit;  
  4.   
  5.   
  6.   
  7. public class DelayQueueExample {  
  8.   
  9.     /** 
  10.      * @param args 
  11.      */  
  12.     public static void main(String[] args) {  
  13.         // TODO Auto-generated method stub  
  14.         DelayQueue<DelayedElement>  dq=new DelayQueue<DelayedElement>();  
  15.         long now = System.currentTimeMillis();  
  16.         System.out.println("current time in ms: " + now);  
  17.         DelayedElement ob1=new DelayedElement("e1", now + 1000);  
  18.         DelayedElement ob2=new DelayedElement("e2", now + 5000);  
  19.         DelayedElement ob3=new DelayedElement("e3", now + 1500);  
  20.           
  21.         dq.add(ob1);  
  22.         dq.add(ob2);  
  23.         dq.add(ob3);  
  24.           
  25.         try {  
  26.             Thread.sleep(1);  
  27.         } catch (InterruptedException e) {              
  28.             throw new RuntimeException( e );  
  29.         }  
  30.           
  31.           
  32.         while(dq.size() > 0){  
  33.             try {  
  34.                 DelayedElement e = dq.take();                  
  35.                 System.out.println("current time in ms: " + System.currentTimeMillis() + ", element:" + e.name);  
  36.                   
  37.             } catch (InterruptedException e) {                  
  38.                 throw new RuntimeException( e );  
  39.             }  
  40.               
  41.         }          
  42.     }  
  43.       
  44.     static class DelayedElement implements Delayed {  
  45.         public long time;  
  46.         public String name;  
  47.         public DelayedElement(String name, long time){  
  48.             this.name = name;  
  49.             this.time = time;  
  50.         }  
  51.         @Override  
  52.         public int compareTo(Delayed o) {  
  53.             // TODO Auto-generated method stub  
  54.             if(this.time < ((DelayedElement)o).time) return -1;  
  55.             else if(this.time > ((DelayedElement)o).time)return 1;  
  56.             else return 0;  
  57.         }  
  58.   
  59.         @Override  
  60.         public long getDelay(TimeUnit unit) {  
  61.             // TODO Auto-generated method stub  
  62.             long r =  unit.convert(time - System.currentTimeMillis(), TimeUnit.NANOSECONDS);  
  63.             //System.out.println("delay:" + r);  
  64.             return r;  
  65.         }  
  66.           
  67.     }  
  68. }  

注意getDelay(TimeUnit unit),这个是实现Delayed这个接口时候必须要实现的一个方法,容易遇坑的地方就是

Java代码  
  1. 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

那么会有什么问题呢?

Java代码  
  1. Retrieves and removes the head of this queue, waiting if necessary until an element with an expired delay is available on this queue.  
  2. Returns:  
  3. the head of this queue  
  4. Throws:  
  5. java.lang.InterruptedException  
  6. 181  
  7. 182    public E take() throws InterruptedException {  
  8. 183        final ReentrantLock lock = this.lock;  
  9. 184        lock.lockInterruptibly();  
  10. 185        try {  
  11. 186            for (;;) {  
  12. 187                E first = q.peek();  
  13. 188                if (first == null) {  
  14. 189                    available.await();  
  15. 190                } else {  
  16. 191                    long delay =  first.getDelay(TimeUnit.NANOSECONDS);  
  17. 192                    if (delay > 0) {  
  18. 193                        long tl = available.awaitNanos(delay);  
  19. 194                    } else {  
  20. 195                        E x = q.poll();  
  21. 196                        assert x != null;  
  22. 197                        if (q.size() != 0)  
  23. 198                            available.signalAll(); // wake up other takers  
  24. 199                        return x;  
  25. 200  
  26. 201                    }  
  27. 202                }  
  28. 203            }  
  29. 204        } finally {  
  30. 205            lock.unlock();  
  31. 206        }  
  32. 207    }  


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

如果打印出每次delay的值便可以看到用nanos多做了多少次循环,读者可以自己看一下,呵呵。

遇到这个坑的人我google到有:

http://www.blogjava.net/killme2008/archive/2010/10/22/335897.html 
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Java 并发工具箱之concurrent包
10分钟搞定 Java 并发队列
实现延时任务的 4 种实现方案!
Java之TimeUnit
JAVA Metrics 度量工具使用介绍1
java中queue的使用
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服