• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

防抖/节流(java)

武飞扬头像
hijunmeng
帮助1

防抖/节流(java)

  • 我们经常需要在回调比较频繁的地方过滤掉一些回调,否则频繁处理会极大影响性能,这就需要对频繁回调的方法进行节流,这部分其实Rxjava已经有线程的方法了,但如果仅仅为了使用这些方法就引入rxjava库,又显得太庞大了

分析

  • 一般我们的使用场景可以分为如下四类:
    • 1 在指定时间内点击指定次数则执行逻辑
    • 2 在指定时间内只执行第一次回调的逻辑
    • 3 在指定时间内只执行最后一次回调的逻辑
    • 4 在指定时间内只执行第一次和最后一次回调的逻辑
  • 第一二种都比较好实现,就是这个最后一次比较难处理,试想下你怎么知道在某段时间内是否是最后一次回调,这好像是做不到的
  • 那么就只能换个思路,先把回调都缓存起来(新回调覆盖旧回调),然后在指定时间后取这个缓存进行触发即可
  • 但是这种方法有个严重问题,就是必须等到指定时间后才会触发,比如只回调一次的话,我们肯定希望立马就执行处理逻辑,但是在时间未到的情况下,我们并不知道这是最后一次回调,因此程序就只能等到时间到了再执行处理逻辑,这就导致执行逻辑会延后处理,这种方法虽然有延后的问题,但只要时间不要设得太大,也是可以接受的
  • 以下就提供纯java实现,也已经发布到码云上了,欢迎使用throttle: java防抖库
import java.util.HashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 节流
 * 使用场景:在回调比较频繁的情况下,用户可能只需要在某段时间内处理第一个或最后一个回调即可,例如按钮的快速点击,关键字搜索,点击指定次数等
 */
public class ThrottleUtil {

    private static ScheduledExecutorService scheduledExecutorService;
    private static volatile boolean isInit = false;

    private static HashMap<Long, Long> cacheLastTime = new HashMap<>();
    private static HashMap<Long, Boolean> cacheSchedule = new HashMap<>();
    private static HashMap<Long, CallbackParam> cacheCallback = new HashMap<>();
    private static HashMap<Long, Integer> cacheCount = new HashMap<>();

    private static void init() {
        if (isInit) {
            return;
        }
        scheduledExecutorService = new ScheduledThreadPoolExecutor(1, new ThreadPoolExecutor.DiscardPolicy());
        isInit = true;
    }

    /**
     * 在某段时间内,只在第一次返回true
     * 默认以线程id作为同一组的key,如果不是同一线程的调用此方法,则请使用{@link ThrottleUtil#throttleFirst(long, long)}传入groupKey
     *
     * @param ms 单位毫秒
     * @return 返回true则执行
     */
    public static boolean throttleFirst(long ms) {
        return throttleFirst(ms, Thread.currentThread().getId());
    }

    /**
     * 在某段时间内,只在第一次返回true
     *
     * @param ms       单位毫秒
     * @param groupKey 同一处回调保持groupKey一致即可
     * @return 返回true则执行
     */
    public static synchronized boolean throttleFirst(long ms, long groupKey) {
        if (cacheLastTime.get(groupKey) == null) {
            cacheLastTime.put(groupKey, 0L);
        }
        long now = System.currentTimeMillis();
        if (now - cacheLastTime.get(groupKey) > ms) {
            cacheLastTime.put(groupKey, now);
            return true;
        }
        return false;
    }

    /**
     * 在指定时间内调用此方法指定次数则返回true
     * 例如,用于在2秒内点击按钮5次则触发事件的业务场景
     *
     * @param count    指定次数
     * @param ms       指定时间,单位毫秒
     * @param groupKey 同一处回调保持groupKey一致即可
     * @return
     */
    public static synchronized boolean throttleCount(int count, long ms, long groupKey) {
        if (cacheLastTime.get(groupKey) == null) {
            cacheLastTime.put(groupKey, 0L);
        }
        if (cacheCount.get(groupKey) == null) {
            cacheCount.put(groupKey, 0);
        }
        long now = System.currentTimeMillis();
        if (cacheCount.get(groupKey) == 0) {
            cacheLastTime.put(groupKey, now);
        }
        if (now - cacheLastTime.get(groupKey) < ms) {
            int tmp = cacheCount.get(groupKey);
            tmp  ;
            cacheCount.put(groupKey, tmp);
            if (tmp >= count) {
                cacheCount.put(groupKey, 0);
                return true;
            }
        } else {
            cacheCount.put(groupKey, 0);
        }
        return false;
    }

    /**
     * 在某段时间内,只触发第一次
     *
     * @param callback 触发回调
     * @param ms       单位毫秒
     */
    public static void throttleFirst(Runnable callback, long ms) {
        throttleFirst(callback, ms, Thread.currentThread().getId());
    }

    /**
     * 在某段时间内,只触发第一次
     *
     * @param callback 触发回调
     * @param ms       单位毫秒
     */
    public static void throttleFirst(Runnable callback, long ms, long groupKey) {
        if (throttleFirst(ms, groupKey)) {
            callback.run();
        }
    }

    /**
     * 在某段时间内,只触发第一次
     *
     * @param callback 触发回调
     * @param userData 用户自定义数据(可为null),会在回调中原样返回
     * @param ms       单位毫秒
     * @param groupKey 同一处回调保持groupKey一致即可
     * @param <T>
     */
    public static <T> void throttleFirst(ICallback<T> callback, T userData, long ms, long groupKey) {
        if (throttleFirst(ms, groupKey)) {
            callback.callback(userData);
        }
    }

    /**
     * 在某段时间内,只触发最后一次
     *
     * @param callback 触发回调
     * @param ms       单位毫秒
     */
    public static void throttleLast(Runnable callback, long ms) {
        throttleLast(callback, ms, Thread.currentThread().getId());
    }

    /**
     * 在某段时间内,只触发最后一次
     *
     * @param callback 触发回调
     * @param ms       单位毫秒
     * @param groupKey 同一处回调保持groupKey一致即可
     */
    public static synchronized void throttleLast(Runnable callback, long ms, long groupKey) {
        throttleLast(userData -> callback.run(), null, ms, groupKey);
    }

    /**
     * 在某段时间内,只触发最后一次
     *
     * @param callback 触发回调
     * @param userData 用户自定义数据(可为null),会在回调中原样返回
     * @param ms       单位毫秒
     * @param groupKey 同一处回调保持groupKey一致即可
     * @param <T>
     */
    public static synchronized <T> void throttleLast(ICallback<T> callback, T userData, long ms, long groupKey) {
        if (cacheSchedule.get(groupKey) == null) {
            cacheSchedule.put(groupKey, false);
        }
        if (!isInit) {
            init();
        }
        cacheCallback.put(groupKey, new CallbackParam(callback, userData));
        if (!cacheSchedule.get(groupKey)) {
            cacheSchedule.put(groupKey, true);
            scheduledExecutorService.schedule(() -> {
                cacheSchedule.put(groupKey, false);
                CallbackParam cp = cacheCallback.get(groupKey);
                if (cp != null) {
                    cp.iCallback.callback(cp.userData);
                }
            }, ms, TimeUnit.MILLISECONDS);
        }
    }


    /**
     * 在某段时间内,只触发第一次和最后一次
     * 默认以线程id为groupKey
     *
     * @param callback 触发回调
     * @param ms       单位毫秒
     */
    public static void throttleFirstAndLast(Runnable callback, long ms) {
        throttleFirstAndLast(callback, ms, Thread.currentThread().getId());
    }

    /**
     * 在某段时间内,只触发第一次和最后一次
     *
     * @param callback 触发的回调
     * @param ms       单位毫秒
     * @param groupKey 同一处回调保持groupKey一致即可
     */
    public static synchronized void throttleFirstAndLast(Runnable callback, long ms, long groupKey) {
        throttleFirstAndLast(userData -> callback.run(), null, ms, groupKey);
    }

    /**
     * 在某段时间内,只触发第一次和最后一次
     *
     * @param callback 触发的回调
     * @param userData 用户自定义数据(可为null),会在回调中原样返回
     * @param ms       单位毫秒
     * @param groupKey 同一处回调保持groupKey一致即可
     * @param <T>
     */
    public static synchronized <T> void throttleFirstAndLast(ICallback<T> callback, T userData, long ms, long groupKey) {
        if (callback == null) {
            return;
        }
        if (cacheSchedule.get(groupKey) == null) {
            cacheSchedule.put(groupKey, false);
        }
        if (cacheCount.get(groupKey) == null) {
            cacheCount.put(groupKey, 0);
        }
        if (!isInit) {
            init();
        }
        if (throttleFirst(ms)) {
            cacheCount.put(groupKey, 0);
            callback.callback(userData);
        } else {
            cacheCount.put(groupKey, cacheCount.get(groupKey)   1);
        }

        cacheCallback.put(groupKey, new CallbackParam(callback, userData));
        if (!cacheSchedule.get(groupKey)) {
            cacheSchedule.put(groupKey, true);
            scheduledExecutorService.schedule(() -> {
                cacheSchedule.put(groupKey, false);
                if (cacheCount.get(groupKey) != 0) {
                    CallbackParam cp = cacheCallback.get(groupKey);
                    if (cp != null) {
                        cp.iCallback.callback(cp.userData);
                    }
                }

            }, ms, TimeUnit.MILLISECONDS);
        }
    }

    public interface ICallback<T> {
        /**
         * 回调
         *
         * @param userData 可为null
         */
        void callback(T userData);
    }

    public static class CallbackParam<T> {
        public ICallback iCallback;
        public T userData;

        public CallbackParam(ICallback iCallback, T userData) {
            this.iCallback = iCallback;
            this.userData = userData;
        }
    }
}
学新通

参考

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgcceek
系列文章
更多 icon
同类精品
更多 icon
继续加载