延迟队列DelayQueue take() 源码分析

版权声明:https://blog.csdn.net/qq_33249725 https://blog.csdn.net/qq_33249725/article/details/89159425

延迟队列DelayQueue take() 源码分析

  • 在工作中使用了延迟队列,对其内部的实现很好奇,于是就研究了一下其运行原理,在这里就介绍一下take()方法的源码

1 take()源码 如下所示

public E take() throws InterruptedException {
		// 加锁的一个动作 保证获取数据的安全性
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            for (;;) {
            	//peek 方法是去头部数据即第一个数据
                E first = q.peek();
                if (first == null)
                	//说明队列为空 调用condition.await()方法,会使得当前线程释放lock然后加入到等待队列中
                    available.await();
                else {
                	//如果第一个数据不为空 获取消息体的延迟时间(getDelay() 会在消息体内重写 自定义添加延迟时间)
                    long delay = first.getDelay(NANOSECONDS);
                    if (delay <= 0)
                    	//如果延迟时间 小于等于0 说明已经达到了延迟时间 调用poll方法返回消息体
                        return q.poll();
                    //如果延迟时间不为空的话 说明还需要等待一段时间 此时重新循环 所以讲frist置为空
                    first = null; 
                    if (leader != null)
                    	//这里用到了Leader/Followers模式 有兴趣的话可以去百度一下这个模式
                    	//如果leader 不为空 说明已经有线程在监听 即有线程在优先获取队列的首元素
                    	//释放当前线程获取的锁 加入到等待队列中 即 当前线程变成了Followers
                        available.await();
                    else {
                    	//如果没有leader 说明没有线程在监听(没有线程在优先获取队列的首元素)
                    	// 将当前线程置为leader线程 
                        Thread thisThread = Thread.currentThread();
                        leader = thisThread;
                        try {
                        	//让当前线程最长等待 delay 时间 等待
                            available.awaitNanos(delay);
                        } finally {
                        	//释放leader权限
                            if (leader == thisThread)
                                leader = null;
                        }
                    }
                }
            }
        } finally {
        	// 如果leader 为空 且 队列中有数据 说明没有其他线程在在等待 
            if (leader == null && q.peek() != null)
            	//唤醒睡眠的线程
                available.signal();
            //释放锁
            lock.unlock();
        }
    }

注意事项:
一开始不明白为什么将frist置为NULL,后面在网上找了相关的资料了解到,如果不讲first置为Null会导致内存泄漏的问题,具体原因如下所示:

  • 如果不将first置为Null,线程A到达,队首元素还没到出列时间,设置线程A = leader
  • 线程B来了因为leader不为空 则会阻塞,后续线程一样。
  • 假如线程A阻塞完毕之后获取列首元素成功出列,这个时候列首元素应该被回收,但是它还被线程B C …所持有一直不会被回收就导致了内存泄漏(gc一直无法回收frist这个对象)这个情况。

猜你喜欢

转载自blog.csdn.net/qq_33249725/article/details/89159425