- 基于Leader-Follower模式的变体,用于尽量减少不必要的线程等待
*/
private Thread leader = null;
/**
- 与lock对应的条件变量
*/
private final Condition available = lock.newCondition();
}
-
使用ReentrantLock独占锁实现线程同步,使用Condition实现等待通知机制。
-
基于Leader-Follower模式的变体,减少不必要的线程等待。
-
内部使用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 中,业务中没有大量数据做并发,缺少实战经验,对并发仅仅停留在了解,做不到精通,所以总是与大厂擦肩而过。
我把私藏的这套并发体系的笔记和思维脑图分享出来,理论知识与项目实战的结合,我觉得只要你肯花时间用心学完这些,一定可以快速掌握并发编程。
不管是查缺补漏还是深度学习都能有非常不错的成效,需要的话记得帮忙点个赞支持一下
整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~