题目
可以添加任务,任务包含任务数据,任务延迟触发的等待时间。
在任务到达触发时间点时,自动触发执行此任务。
队列中任务保持先进先出原则:假设 A 任务的触发等待时间为 X,B 任务的触发等待时间为 Y,B 在 A 之后被添加入队列,则 A 的前驱任务执行完成后等待时间 X 后,才执行 A,同理在 A 执行完成后,等待时间 Y,才执行 B。
思路过程
1.Java上线
读题目就是延时队列的特征,Java有锁,有多线程,写起来多方便
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
public class HandWritingQueue {
public static void main(String[] args) {
final BlockingQueue<DelayedElement> deque = new DelayQueue<>();
Runnable producerRunnable = new Runnable() {
int i = 10;
public void run() {
while (true && i>0) {
try {
--i;
System.out.println("producing "+i+",wait "+i+" seconds");
deque.put(new DelayedElement(1000 * i, "i=" + i));
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Runnable customerRunnable = new Runnable() {
public void run() {
while (true) {
try {
System.out.println("consuming:" + deque.take().msg);
//Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Runnable getSize= new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("size="+deque.size());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread thread1 = new Thread(producerRunnable);
thread1.start();
Thread thread2 = new Thread(customerRunnable);
thread2.start();
Thread thread3 = new Thread(getSize);
thread3.start();
}
static class DelayedElement implements Delayed {
private final long expire;
private final String msg;
public DelayedElement(long delay, String msg) {
this.msg = msg;
expire = System.currentTimeMillis() + delay;
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(this.expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
return -1;//FIFO
}
}
}
2.Node上线
被提醒该题目可以用node实现,且不需要借助redis来做,然后我上手就是一把操作:
'use strict'
class DelayElement {
constructor(data, expire) {
this.data = data;
this.expire = expire;//second
}
}
const delayArray = [];
//push two element in delayArray
delayArray.push(new DelayElement(1, 2));
delayArray.push(new DelayElement(2, 1));
let length = delayArray.length;
let time_cnt = 0;
while (delayArray.length > 0) {
let de = delayArray.shift();
time_cnt += de.expire;//serial
(function () {
setTimeout(() => {
console.log('expire data is :' + de.data + ',expire time is :' + de.expire);
}, time_cnt * 1000);
})();
}
我以为设计的考点也就是立即执行函数,延时的使用,但是这里的for循环是个伪串行,实际上是并发的,也为第三步的修改提供了bug
3.Promise时代
一开始我是想把async函数放进去,写了如下的代码:
'use strict'
const delayArray = [];
const daPush = (data, expire) => {
delayArray.push(async () => {
setTimeout(() => {
console.log('data is ' + data + ' and expire is ' + expire);
}, expire * 1000);
});
}
daPush(1, 4);//2 seconds
daPush(2, 5);
(async () => {
for (const da of delayArray) {
await da();
}
})();
发现代码还是串行的,然后查了一下可能的问题(以下为个人猜测,欢迎指正)async声明的函数会包装成Promise不假,但是for循环会并发去执行await中的async
4.正解
promise执行会阻塞主线程
'use strict'
const delayArray = [];
const daPush = (data, expire) => {
delayArray.push(() => new Promise((resolve,reject) => {
setTimeout(() => {
if(data)
{
console.log('data is ' + data + ' and expire is ' + expire);
resolve(true);
}
else{
reject('there is nodata');
}
}, expire * 1000);
}));
};
daPush(1, 4);//2 seconds
daPush(2, 5);
(async () => {
for (const da of delayArray) {
da().then((value)=>{
// console.log(value);
}).catch((value)=>{
console.log(value);
});
//没有28-33,只35行也可以
// await da();
}
})();