• Java节流阀设计与实现


    介绍

    前言

      我首次接触到节流阀是编写前端的时候遇到的,当时的需求是给Javascriptwindow.onsize事件绑定函数,当浏览器页面大小被改变时调用,但是存在一个问题:用鼠标拖拽的形式改变浏览器页面大小的过程是连续的,也就是window.onsize事件在短时间内被频繁触发。当回调频繁执行时,页面可能会变卡,所以此时节流阀解决了这一个问题。

    特点:

    • 在函数/方法被短时间内频繁调用的时候,限制执行频率
    • 短时间内只调用一次或者连续调用时最后一次都会被执行

    比如有个按钮,每隔1ms按下一次。如果设置执行频率为10ms执行一次,那么连续按下按钮,也只会10ms按钮内回调事件生效一次。同时为了保证最后一次生效、或者比较长的时间内按钮只按下了一次,则这一次也需要生效。

    编码

    JS版本代码

    这里先贴一个Javascript版本的节流阀代码

    //节流阀
    export function throttle(delay,callback) {
        let startTime = new Date().getTime();
        let timer = null;
        return function () {
            const currentTime = new Date().getTime();
            clearTimeout(timer)
            //console.log("时间差:", currentTime - startTime);
            if (currentTime - startTime >= delay) {
                callback();
                startTime = currentTime; // 注意:是startTime=curerntTime!
                // 不是currentTime=startTime
            } else {
                timer = setTimeout(function () {
                    callback();
                }, delay)
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    参数说明

    delay:连续调用时的执行频率,单位为ms
    callback:被调用的回调函数

    代码解读:

    这种写法使用的Javascript闭包,函数内部的startTimetimer并没有因为函数调用结束之后被销毁,还能够继续被return function(){...}内部的函数使用(可能产生内存泄漏)。
    当返回的function被调用的时候,会获取当前时间,同时销毁上一次调用时创建的延迟函数,计算当前时间与上一次被调用时(或初始化时的时间)时间差是否大于等于delay,若是则执行回调,更新上一次被调用的时间,若不是,则新建延迟函数。
    延时函数主要用于函数只调用一次以及最后一次调用都会被执行

    Java版本

    由于Javascript是单线程的,不需要考虑线程安全问题,而使用Java编写节流阀需要考虑这个。
    为了能够使用Javascript里面接收function参数和return function返回值写法,需要使用Java1.8Lambda语法糖和函数式接口

    涉及知识点

    • lambda语法糖
    • 函数式接口
    • 线程安全
    • 线程中断
    • 闭包

    接口代码

    package lambda;
    
    import java.util.concurrent.atomic.AtomicLong;
    import java.util.concurrent.atomic.AtomicReference;
    
    @FunctionalInterface
    public interface Throttle{
    
        /**
         *
         * @param callback 回调函数
         * @param delay 流速控制时间,单位毫秒
         * @return 使用lambda和闭包构造的匿名节流阀对象
         */
        static Throttle bulid(Throttle callback,long delay){
            AtomicLong startTime= new AtomicLong(System.currentTimeMillis());
            AtomicReference<Thread> thread= new AtomicReference<>();
            return () -> {
                final long currentTime=System.currentTimeMillis();
                synchronized (callback){
                    if(thread.get() !=null) thread.get().interrupt();
                    if(currentTime - startTime.get() >=delay){
                        callback.callback();
                        startTime.set(currentTime);
                    }else{
                        thread.set(new Thread(() -> {
                            try {
                                Thread.sleep(delay);
                            } catch (InterruptedException ignored) {
                                return;
                            }
                            callback.callback();
                        }));
                        thread.get().start();
                    }
                }
            };
        }
    
        //执行节流阀
        void callback();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    使用方法

    public class ThrottleTest {
        public static void main(String[] args) {
        
            //创建:1.使用匿名内部类写法
            Throttle bulid = Throttle.bulid(new Throttle() {
                @Override
                public void callback() {
                    //TODO
                }
            }, 100);
    
            //创建:2.使用lambda写法
            bulid = Throttle.bulid(() -> {
                //TODO
            }, 100);
            
            //调用
            bulid.callback();    
        }
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    测试

    package lambda;
    
    
    public class ThrottleTest {
        public static void main(String[] args) {
            ThrottleTest  test= new ThrottleTest();
            test.test01();
            //test.test02();
        }
        public void test01(){
            Throttle throttle = Throttle.bulid(() -> System.out.println(System.currentTimeMillis()%1000), 50);
            int count=0;
            while(count++<500){
                throttle.callback();
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        public void test02(){
            Throttle throttle = Throttle.bulid(() -> System.out.println(System.currentTimeMillis()%1000), 50);
            Thread[] threads=new Thread[10];
            for (int i = 0; i < threads.length; i++) {
                threads[i] = new Thread(() -> {
                    int count = 0;
                    while (count++ < 500) {
                        throttle.callback();
                        try {
                            Thread.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
            for (Thread thread : threads) {
                thread.start();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    test01()执行结果:

    282
    333
    385
    436
    486
    536
    587
    637
    688
    740
    790
    841
    891
    941
    991
    42
    92
    143
    244
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    test02()执行结果:

    898
    948
    999
    49
    99
    149
    199
    249
    299
    350
    400
    450
    500
    550
    600
    650
    700
    750
    800
    850
    930
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    VS_QT_4_Qt设计师
    8.11-分析工具、8.12-数据字典、8.13-数据流图 8.14-设计工具
    【JKI SMO】框架讲解(二)
    建陶行业标杆『诺贝尔瓷砖』&企企通供应链协同系统一期项目上线,数字采购领航企业高质量发展
    SpringBoot项目连接MySQL数据库
    全新叙事赛道:诺亚引领不良资产合成潮流,DeFi生态再添“万亿”动力
    mysql存储过程
    【PHPWord】PHPOffice 套件之PHPWord快速入门
    【试题040】多个逻辑或例题2
    [C#]JCoder.Db4Net.ORM,基于JCoder.Db4Net的ORM库,轻量的MIT协议类库
  • 原文地址:https://blog.csdn.net/qq_16525829/article/details/127569210