題目
可以添加任務,任務包含任務數據,任務延遲觸發的等待時間。
在任務到達觸發時間點時,自動觸發執行此任務。
隊列中任務保持先進先出原則:假設 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();
}
})();