`
standalone
  • 浏览: 595983 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

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

    博客分类:
  • java
阅读更多
今天不忙,学习一下java.util.concurrent.DelayQueue这个类的使用。参照了
http://www.concretepage.com/java/example_delayqueue_java.php
上的例子,但是这里有个坑。

先看一下整个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<DelayedElement>  dq=new DelayQueue<DelayedElement>();
        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

那么会有什么问题呢?

Retrieves and removes the head of this queue, waiting if necessary until an element with an expired delay is available on this queue.
Returns:
the head of this queue
Throws:
java.lang.InterruptedException
181
182    public E take() throws InterruptedException {
183        final ReentrantLock lock = this.lock;
184        lock.lockInterruptibly();
185        try {
186            for (;;) {
187                E first = q.peek();
188                if (first == null) {
189                    available.await();
190                } else {
191                    long delay =  first.getDelay(TimeUnit.NANOSECONDS);
192                    if (delay > 0) {
193                        long tl = available.awaitNanos(delay);
194                    } else {
195                        E x = q.poll();
196                        assert x != null;
197                        if (q.size() != 0)
198                            available.signalAll(); // wake up other takers
199                        return x;
200
201                    }
202                }
203            }
204        } finally {
205            lock.unlock();
206        }
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
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics