Java并发源码:阻塞队列实现之DelayQueue源码解析

  • 基于Leader-Follower模式的变体,用于尽量减少不必要的线程等待

*/

private Thread leader = null;

/**

  • 与lock对应的条件变量

*/

private final Condition available = lock.newCondition();

}

  1. 使用ReentrantLock独占锁实现线程同步,使用Condition实现等待通知机制。

  2. 基于Leader-Follower模式的变体,减少不必要的线程等待。

  3. 内部使用PriorityQueue优先级队列存储元素,且队列中元素必须实现Delayed接口。

Delayed接口

=============

队中的元素必须实现Delayed接口【Delay接口又继承了Comparable,需要实现compareTo方法】,每个元素都需要指明过期时间,通过getDelay(unit)获取元素剩余时间【剩余时间 = 到期时间 - 当前时间】。

每次向优先队列中添加元素时根据compareTo方法作为排序规则,当然我们约定一下,默认q.peek()出来的就是最先过期的元素。

public interface Delayed extends Comparable {

// 返回剩余时间

long getDelay(TimeUnit unit);

}

public interface Comparable {

// 定义比较方法

public int compareTo(T o);

}

Delayed元素案例

===============

学习了Delayed接口之后,我们看一个实际的案例,加深印象,源于:《Java并发编程之美》。

static class DelayedElement implements Delayed {

private final long delayTime; // 延迟时间

private final long expire; // 到期时间

private final String taskName; // 任务名称

public DelayedElement (long delayTime, String taskName) {

this.delayTime = delayTime;

this.taskName = taskName;

expire = now() + delayTime;

}

final long now () {

return System.currentTimeMillis();

}

// 剩余时间 = 到期时间 - 当前时间

@Override

public long getDelay (TimeUnit unit) {

return unit.convert(expire - now(), TimeUnit.MILLISECONDS);

}

@Override

public int compareTo (Delayed o) {

return (int) (getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));

}

@Override

public String toString () {

final StringBuilder res = new StringBuilder("DelayedElement [ ");

res.append("delay = ").append(delayTime);

res.append(", expire = ").append(expire);

res.append(“, taskName = '”).append(taskName).append(‘’');

res.append(" ] ");

return res.toString();

}

}

public static void main (String[] args) {

// 创建delayQueue队列

DelayQueue delayQueue = new DelayQueue<>();

// 创建延迟任务

Random random = new Random();

for (int i = 0; i < 10; i++) {

DelayedElement element = new DelayedElement(random.nextInt(500), "task: " + i);

delayQueue.offer(element);

}

// 依次取出任务并打印

DelayedElement ele = null;

try {

for (; ; ) {

while ((ele = delayQueue.take()) != null) {

System.out.println(ele);

}

}

} catch (InterruptedException ex) {

ex.printStackTrace();

}

}

// 打印结果

DelayedElement [ delay = 2, expire = 1611995426061, taskName = ‘task: 4’ ]

DelayedElement [ delay = 52, expire = 1611995426111, taskName = ‘task: 2’ ]

DelayedElement [ delay = 80, expire = 1611995426139, taskName = ‘task: 5’ ]

DelayedElement [ delay = 132, expire = 1611995426191, taskName = ‘task: 0’ ]

DelayedElement [ delay = 174, expire = 1611995426233, taskName = ‘task: 9’ ]

DelayedElement [ delay = 175, expire = 1611995426234, taskName = ‘task: 7’ ]

DelayedElement [ delay = 326, expire = 1611995426385, taskName = ‘task: 3’ ]

DelayedElement [ delay = 447, expire = 1611995426506, taskName = ‘task: 8’ ]

DelayedElement [ delay = 452, expire = 1611995426511, taskName = ‘task: 1’ ]

DelayedElement [ delay = 486, expire = 1611995426545, taskName = ‘task: 6’ ]

  • 实现了compareTo方法,定义比较规则为越早过期的排在队头。

  • 实现了getDelay方法,计算公式为:剩余时间 = 到期时间 - 当前时间。

构造器

=======

DelayQueue构造器相比于前几个,就显得非常easy了。

public DelayQueue() {}

public DelayQueue(Collection<? extends E> c) {

this.addAll©;

}

void put(E e)

=================

因为DelayQueue是无界队列,不会因为边界问题产生阻塞,因此put操作和offer操作是一样的。

public void put(E e) {

offer(e);

}

public boolean offer(E e) {

// 获取独占锁

final ReentrantLock lock = this.lock;

lock.lock();

try {

// 加入优先队列里

q.offer(e);

// 判断堆顶元素是不是刚刚插入的元素

// 如果判断为true,说明当前这个元素是将最先过期

if (q.peek() == e) {

// 重置leader线程为null

leader = null;

// 激活available变量条件队列中的一个线程

available.signal();

}

return true;

} finally {

lock.unlock();

}

}

E take()

============

take方法将会获取并移除队列里面延迟时间过期的元素 ,如果队列里面没有过期元素则陷入等待。

public E take() throws InterruptedException {

// 获取独占锁

final ReentrantLock lock = this.lock;

lock.lockInterruptibly();

try {

for (;😉 {

// 瞅一瞅谁最快过期

E first = q.peek();

// 队列为空,则将当前线程置入available的条件队列中,直到里面有元素

if (first == null)

available.await();

else {

// 看下还有多久过期

long delay = first.getDelay(NANOSECONDS);

// 哇,已经过期了,就移除它并返回

if (delay <= 0)

return q.poll();

first = null; // don’t retain ref while waiting

// leader不为null表示其他线程也在执行take

最后

很多程序员,整天沉浸在业务代码的 CRUD 中,业务中没有大量数据做并发,缺少实战经验,对并发仅仅停留在了解,做不到精通,所以总是与大厂擦肩而过。

我把私藏的这套并发体系的笔记和思维脑图分享出来,理论知识与项目实战的结合,我觉得只要你肯花时间用心学完这些,一定可以快速掌握并发编程。

不管是查缺补漏还是深度学习都能有非常不错的成效,需要的话记得帮忙点个赞支持一下

整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~

  • 24
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值