JAVA實現節流閥
在前端開發中,有種效果叫做節流閥
顧名思義,節流閥的作用是限制某些事件的執行頻率,基本代碼如下:
obj.event = function(){
clearTimeout(obj.timer);
obj.timer = setTimeout(() => {
execute
}, timeout);
}
JAVA實現
在JAVA中,我們也需要一些類似的效果
抽象
我們抽象出一個pool,這個pool可以接受你的任務,如果是同一個任務,會覆蓋之前還沒執行的任務
class Pool {
void execute(String key,Runnable runnable,long delay){}
}
這裏的key就代表你的任務,delay就是任務的間隔時間
使用線程池
我們可以引入線程池來簡化編碼
private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(6);
線程池的作用是進行任務調度,ScheduledExecutorService允許我們提交一個延遲執行的任務
思路
當程序員提交一個任務,pool會判斷這個任務是否在pool中待執行,
如果是待執行,則取消pool中的任務,後用新提交的這個任務替代它
scheduledExecutorService提交任務會返回一個future,在執行之前,我們可以取消它
完整代碼如下
void execute(String key,Runnable runnable,long delay){
ScheduledFuture<?> task = map.get(key);
if (task != null){
task.cancel(false);
}
ScheduledFuture<?> schedule = scheduledExecutorService.schedule(()->{
runnable.run();
map.remove(key);
}, delay, TimeUnit.MILLISECONDS);
map.put(key,schedule);
}
這裏使用了一個map來記住任務的執行情況
測試
- 第一個測試
// 應該只會輸出一個2
List<Integer> list = new ArrayList<>();
CountDownLatch latch = new CountDownLatch(1);
Pool pool = new Pool();
for (int i = 0; i < 3; i++) {
int finalI = i;
pool.execute("task",()->{
System.out.println(finalI);
list.add(finalI);
latch.countDown();
},1000);
Thread.sleep(800);
}
latch.await();
assertEquals(1,list.size());
assertEquals(2,list.get(0));
這裏依次執行三次任務,但是這個任務的延遲時間是1000,任務執行的間隔是800
間隔比延遲實現還要小,所以前面2個任務會被忽略,結果只會輸出2
- 第二個測試
// 應該輸出 0 1 2
List<Integer> list = new ArrayList<>();
CountDownLatch latch = new CountDownLatch(3);
Pool pool = new Pool();
for (int i = 0; i < 3; i++) {
int finalI = i;
pool.execute("task",()->{
System.out.println(finalI);
list.add(finalI);
latch.countDown();
},1000);
Thread.sleep(1100);
}
latch.await();
assertEquals(3,list.size());
assertEquals(3,list.size());
這個測試任務間隔比延遲還要大,所以三個任務會被依次執行
輸出 0 1 2