<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
當持續觸發事件時,一定時間段內沒有再觸發事件,事件處理常式才會執行一次,如果設定時間到來之前,又觸發了事件,就重新開始延時。
當持續觸發事件時,保證在一定時間內只呼叫一次事件處理常式,意思就是說,假設一個使用者一直觸發這個函數,且每次觸發小於既定值,函數節流會每隔這個時間呼叫一次。
想到這裡,很多人就會想到一個作用,沒錯,就是防重複提交。但是這個業務時間比較難把控,所以還是建議用它來做一些無狀態的操作比較好。比如說:重新整理排行榜,前端看似一直在點選,其實後端為了防止介面崩掉,每1秒才執行真正的一次重新整理。
防抖是將多次執行變為指定時間內不在觸發之後,執行一次。
節流是將多次執行變為指定時間不論觸發多少次,時間一到就執行一次
java實現防抖和節流的關鍵是Timer類和Runnable介面。
其中,Timer中關鍵方法cancel() 實現防抖 schedule() 實現節流。下面簡單介紹一下這兩個方法。
Timer##cancel():Timer.cancel() 被呼叫之後整個Timer 的 執行緒都會結束掉。
這就很好理解了,既然是做防抖,只要你在指定時間內觸發,我直接 cancel()掉,就是取消掉,不讓他執行。
Timer##schedule():使用者呼叫 schedule() 方法後,要等待N秒的時間才可以第一次執行 run() 方法。
這個N是我們根據業務評估出來的時間,作為引數傳進去。
package com.example.test01.zhangch; import java.util.Timer; import java.util.TimerTask; /** * @Author zhangch * @Description java 防抖 * @Date 2022/8/4 18:18 * @Version 1.0 */ @SuppressWarnings("all") public class DebounceTask { /** * 防抖實現關鍵類 */ private Timer timer; /** * 防抖時間:根據業務評估 */ private Long delay; /** * 開啟執行緒執行任務 */ private Runnable runnable; public DebounceTask(Runnable runnable, Long delay) { this.runnable = runnable; this.delay = delay; } /** * * @param runnable 要執行的任務 * @param delay 執行時間 * @return 初始化 DebounceTask 物件 */ public static DebounceTask build(Runnable runnable, Long delay){ return new DebounceTask(runnable, delay); } //Timer類執行:cancel()-->取消操作;schedule()-->執行操作 public void timerRun(){ //如果有任務,則取消不執行(防抖實現的關鍵) if(timer!=null){ timer.cancel(); } timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { //把 timer 設定為空,這樣下次判斷它就不會執行了 timer=null; //執行 runnable 中的 run()方法 runnable.run(); } }, delay); } }
可以看到,測試中,我 1 毫秒請求一次,這樣的話,1秒內都存在連續請求,防抖操作永遠不會執行。
public static void main(String[] args){ //構建物件,1000L: 1秒執行-->1秒內沒有請求,在執行防抖操作 DebounceTask task = DebounceTask.build(new Runnable() { @Override public void run() { System.out.println("防抖操作執行了:do task: "+System.currentTimeMillis()); } },1000L); long delay = 100; while (true){ System.out.println("請求執行:call task: "+System.currentTimeMillis()); task.timerRun(); try { //休眠1毫秒在請求 Thread.sleep(delay); } catch (InterruptedException e) { e.printStackTrace(); } } }
結果如我們所料:
Connected to the target VM, address: '127.0.0.1:5437', transport: 'socket'
請求執行:call task: 1659609433021
請求執行:call task: 1659609433138
請求執行:call task: 1659609433243
請求執行:call task: 1659609433350
請求執行:call task: 1659609433462
請求執行:call task: 1659609433572
請求執行:call task: 1659609433681
請求執行:call task: 1659609433787
請求執行:call task: 1659609433893
請求執行:call task: 1659609433999
請求執行:call task: 1659609434106
請求執行:call task: 1659609434215
請求執行:call task: 1659609434321
請求執行:call task: 1659609434425
請求執行:call task: 1659609434534
測試2中,我們在請求了2秒之後,讓主執行緒休息2秒,這個時候,防抖在1秒內沒有在次觸發,所以就會執行一次防抖操作。
public static void main(String[] args){ //構建物件,1000L:1秒執行 DebounceTask task = DebounceTask.build(new Runnable() { @Override public void run() { System.out.println("防抖操作執行了:do task: "+System.currentTimeMillis()); } },1000L); long delay = 100; long douDelay = 0; while (true){ System.out.println("請求執行:call task: "+System.currentTimeMillis()); task.timerRun(); douDelay = douDelay+100; try { //如果請求執行了兩秒,我們讓他先休息兩秒,在接著請求 if (douDelay == 2000){ Thread.sleep(douDelay); } Thread.sleep(delay); } catch (InterruptedException e) { e.printStackTrace(); } } }
結果如我們所料,防抖任務觸發了一次,休眠結束之後,請求就會在1毫秒內連續觸發,防抖也就不會在次觸發了。
請求執行:call task: 1659609961816
請求執行:call task: 1659609961924
請求執行:call task: 1659609962031
請求執行:call task: 1659609962138
請求執行:call task: 1659609962245
請求執行:call task: 1659609962353
防抖操作執行了:do task: 1659609963355
請求執行:call task: 1659609964464
請求執行:call task: 1659609964569
請求執行:call task: 1659609964678
請求執行:call task: 1659609964784
簡易版:根據新手寫程式碼習慣,對程式碼寫法做了調整,但是不影響整體功能。這種寫法更加符合我這種新手小白的寫法。
public static void main(String[] args){ //要執行的任務,因為 Runnable 是介面,所以 new 物件的時候要實現它的 run方法 Runnable runnable = new Runnable() { @Override public void run() { //執行列印,真實開發中,是這些我們的業務程式碼。 System.out.println("防抖操作執行了:do task: "+System.currentTimeMillis()); } }; //runnable:要執行的任務,通過引數傳遞進去。1000L:1秒執行內沒有請求,就執行一次防抖操作 DebounceTask task = DebounceTask.build(runnable,1000L); //請求持續時間 long delay = 100; //休眠時間,為了讓防抖任務執行 long douDelay = 0; //while 死迴圈,請求一直執行 while (true){ System.out.println("請求執行:call task: "+System.currentTimeMillis()); //呼叫 DebounceTask 防抖類中的 timerRun() 方法, 執行防抖任務 task.timerRun(); douDelay = douDelay+100; try { //如果請求執行了兩秒,我們讓他先休息兩秒,在接著請求 if (douDelay == 2000){ Thread.sleep(douDelay); } Thread.sleep(delay); } catch (InterruptedException e) { e.printStackTrace(); } } }
package com.example.test01.zhangch; import java.util.Timer; import java.util.TimerTask; /** * @Author zhangch * @Description 節流 * @Date 2022/8/6 15:41 * @Version 1.0 */ public class ThrottleTask { /** * 節流實現關鍵類 */ private Timer timer; private Long delay; private Runnable runnable; private boolean needWait=false; /** * 有參建構函式 * @param runnable 要啟動的定時任務 * @param delay 延遲時間 */ public ThrottleTask(Runnable runnable, Long delay) { this.runnable = runnable; this.delay = delay; this.timer = new Timer(); } /** * build 建立物件,相當於 ThrottleTask task = new ThrottleTask(); * @param runnable 要執行的節流任務 * @param delay 延遲時間 * @return ThrottleTask 物件 */ public static ThrottleTask build(Runnable runnable, Long delay){ return new ThrottleTask(runnable, delay); } public void taskRun(){ //如果 needWait 為 false,結果取反,表示式為 true。執行 if 語句 if(!needWait){ //設定為 true,這樣下次就不會再執行 needWait=true; //執行節流方法 timer.schedule(new TimerTask() { @Override public void run() { //執行完成,設定為 false,讓下次操作再進入 if 語句中 needWait=false; //開啟多執行緒執行 run() 方法 runnable.run(); } }, delay); } } }
節流測試,每 2ms 請求一次,節流任務是每 1s 執行一次。真實效果應該是 1s 內前端發起了五次請求,但是後端只執行了一次操作
public static void main(String[] args){ //建立節流要執行的物件,並把要執行的任務傳入進去 ThrottleTask task = ThrottleTask.build(new Runnable() { @Override public void run() { System.out.println("節流任務執行:do task: "+System.currentTimeMillis()); } },1000L); //while一直執行,模擬前端使用者一直請求後端 while (true){ System.out.println("前端請求後端:call task: "+System.currentTimeMillis()); task.taskRun(); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } }
結果如我們所料
前端請求後端:call task: 1659772459363
前端請求後端:call task: 1659772459574
前端請求後端:call task: 1659772459780
前端請求後端:call task: 1659772459995
前端請求後端:call task: 1659772460205
節流任務執行:do task: 1659772460377
前端請求後端:call task: 1659772460409
前端請求後端:call task: 1659772460610
前端請求後端:call task: 1659772460812
前端請求後端:call task: 1659772461027
前端請求後端:call task: 1659772461230
節流任務執行:do task: 1659772461417
idea 爆紅線了,強迫症的我受不了,肯定要解決它
腦子第一時間冒出來的是 @SuppressWarnings("all") 註解,跟所有的警告說拜拜~瞬間就清爽了
算了,壓制警告總感覺是不負責任。總不能這樣草草了事,那就來直面這個爆紅。既然讓我用 ScheduledExecutorService ,那簡單,直接替換
public class ThrottleTask { /** * 節流實現關鍵類: */ private ScheduledExecutorService timer; private Long delay; private Runnable runnable; private boolean needWait=false; /** * 有參建構函式 * @param runnable 要啟動的定時任務 * @param delay 延遲時間 */ public ThrottleTask(Runnable runnable, Long delay) { this.runnable = runnable; this.delay = delay; this.timer = Executors.newSingleThreadScheduledExecutor(); } /** * build 建立物件,相當於 ThrottleTask task = new ThrottleTask(); * @param runnable 要執行的節流任務 * @param delay 延遲時間 * @return ThrottleTask 物件 */ public static ThrottleTask build(Runnable runnable, Long delay){ return new ThrottleTask(runnable, delay); } public void taskRun(){ //如果 needWait 為 false,結果取反,表示式為 true。執行 if 語句 if(!needWait){ //設定為 true,這樣下次就不會再執行 needWait=true; //執行節流方法 timer.schedule(new TimerTask() { @Override public void run() { //執行完成,設定為 false,讓下次操作再進入 if 語句中 needWait=false; //開啟多執行緒執行 run() 方法 runnable.run(); } }, delay,TimeUnit.MILLISECONDS); } } }
那麼定時器 Timer 和 ScheduledThreadPoolExecutor 解決方案之間的主要區別是什麼,我總結了三點...
到此這篇關於詳解Java中的防抖和節流的文章就介紹到這了,更多相關Java防抖 節流內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45