暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Java高并发系列之DelayQueue源码解析

码农的修炼之道 2018-12-12
581

      今天来介绍BlockingQueue接口下的实现类:DelayQueue。这是一个无界阻塞队列,队列中每个元素都要实现Delay接口从而具有过期时间。从队列中取元素时候,只有元素达到过期时间才会出队列。队列头部就是最接近到期时间的元素。此外,DelayQueue内部也用ReentrantLock作为锁。


      下面,我们来看看DelayQueue的类图。

         从类图可以看出,DelayQueue是继承自AbstractQueue,实现了BlockingQueue接口。此外,DelayQueue要求队列元素实现Delay接口。


1、构造函数

   DelayQueue的构造函数有两个,分布是空参数和传入Collection集合。


2、add操作

     add操作是向队列添加元素,其源码如下:

    从源码可以看出,add操作还是调用了offer操作放入元素。下面将介绍offer源码。


3、offer操作

     offer操作也是向队列中添加元素,其源码如下:

       首先,源码中offer获取锁,然后向q中放入元素。这里q是一个优先级队列(PriorityQueue)。由于q是优先级队列,因此向里面存放元素,调用peek方法返回的不一定是当前元素。如果peek返回的是e,那么说明当前压入的元素就是要过期的元素,那么就要唤醒因为执行take操作(取队列元素)而被阻塞的线程。

     另外,offer提供了另一个函数,其底层还是调用上面的offer函数,因此其参数中的timeout不起作用。

5、put操作

       put操作也是向队列中添加元素,源码如下:

        从源码可以看出,put操作也是调用上面的offer函数。这里不再赘述。


6、poll操作

     poll操作是从队列中取元素,其源码如下:

  从源码可以看出,首先进行加锁。然后从队列q中去查看队列首部元素。如果首部元素first为空,或者没有过期。那么就返回空。否则,返回队列首部元素并删除元素。


7、take操作

     take操作也是从队列取出首部元素,源码如下:

       从源码可以看出,首先获取锁。然后进入死循环内,首先获取队列头部元素,如果元素为空,则说明队列没有元素,那么当前线程就会被挂起直到offer操作添加了元素才可能唤醒当前线程。如果头部元素不为空,那么就判断过期时间,如果delay小于0,则说明过期,直接取队列首部元素。如果元素没有过期,那么接着判断lead是否为空(这里lead的作用是保证只有一个线程执行take操作),如果lead不为空,说明有线程在执行take,则挂起当前线程。否则,如果lead为空,则设置lead为当前线程,然后等待元素过期。元素过期后,设置lead为空,最后释放锁。


8、size操作

    从源码可以看出,size操作也进行了加锁,因此size的值是准确的。


总结

1、向队列放入元素是add操作,offer操作,put操作。其中,add和put操作还是调用offer函数。

2、向队列取元素是take操作、poll操作。其中poll操作是非阻塞的,如果队列头部没有元素或者未过期,会返回空。否则会取出队列首部元素。take操作会阻塞,直到有offer操作唤醒。

3、DelayQueue采用了lead-follow模式,保证只有lead线程可以take操作。这种做法保证了不必要的线程等待。比如,一个线程调用take变成lead后,它会调用waitNanos方法等待一段时间,其他线程(follow线程)会调用await进行无限期等待。当lead执行完退出take后,会唤醒阻塞队列中的其他take线程。

    注意:这种lead-follow模式很有用,以后还会接触到。

文章转载自码农的修炼之道,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论