首页 > 其他分享 >【工具类】线程安全的滑动时间窗口记录工具类

【工具类】线程安全的滑动时间窗口记录工具类

时间:2023-05-09 15:55:43浏览次数:57  
标签:java import interval util concurrent 线程 currentCycle 滑动 工具

闲来无事,分享一个工具类,写的不好,轻喷,欢迎指出问题

目标是线程安全无锁高性能的记录滑动时间窗口值

import lombok.Getter;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReferenceArray;

/**
 * 线程安全的滑动时间窗口计数,时间单位:秒
 */
public class SlidingTimeWindow {
    // 时间窗口内的总记录值
    private final AtomicInteger count = new AtomicInteger(0);
    // 每个时间点内的记录值
    private final AtomicReferenceArray<TimeSplit> arr;
    // 时间窗口的大小,单位秒
    private final int interval;
    // 最后一次处理的周期数,主要用于当长时间未被调用时更新数据使用
    private volatile long lastCycle;

    // 窗口时间长度
    public SlidingTimeWindow(int interval) {
        this.interval = interval;
        arr = new AtomicReferenceArray<>(interval);
        this.lastCycle = getCurrentCycle();
    }

    // 窗口时间内的记录值
    public int get() {
        long currentCycle = getCurrentCycle();
        if(currentCycle <= lastCycle) {
            return count.get();
        }
        updateTs(currentCycle);
        return count.get();
    }
    // 窗口时间内的记录值并加1
    public int getAndIncrement() {
        long currentCycle = getCurrentCycle();
        TimeSplit ts = updateTs(currentCycle);
        ts.getCount().getAndIncrement();
        return count.getAndIncrement();
    }

    // 计算所属时间周期
    private long getCurrentCycle() {
        return System.currentTimeMillis() / 1000;
    }

    // 更新时间窗口内的记录值
    private TimeSplit updateTs(long currentCycle) {
        long lastCycleTemp = Math.max(this.lastCycle, currentCycle - interval);
        if(currentCycle > lastCycleTemp) {
            // 更新
            this.lastCycle = currentCycle;
        } else if(currentCycle < lastCycleTemp) {
            // 避免机器发生时间回拨导致的错误
            lastCycleTemp = currentCycle;
        }

        TimeSplit ts = null;
        for(;lastCycleTemp<=currentCycle;lastCycleTemp++) {
            // 依次更新每个时间点的数据
            ts = doUpdateTs(lastCycleTemp);
        }
        return ts;
    }

    /**
     * 更新指定时间点的数据
     * 覆盖已经过期的数据,将过期数据从总记录值中减去
     * @param time
     * @return
     */
    private TimeSplit doUpdateTs(long time) {
        int index = (int)(time % interval);
        TimeSplit ts = arr.get(index);
        while (ts == null || ts.getTime() != time) {
            TimeSplit newTs = new TimeSplit(time,new AtomicInteger(0));
            if(arr.compareAndSet(index,ts,newTs) && ts != null) {
                count.getAndAdd(-ts.getCount().get());
            }
            ts = arr.get(index);
        }
        return ts;
    }

    /**
     * 记录每个时间点的值,
     * 当时间点过期时,用于移除总值中该时间点的记录值
     */
    @Getter
    private class TimeSplit {
        private final long time;
        private final AtomicInteger count;

        public TimeSplit(long time, AtomicInteger count) {
            this.time = time;
            this.count = count;
        }
    }
  // chatgpt帮忙写的测试用例
    public static void main(String[] args) throws InterruptedException {
         int THREAD_POOL_SIZE = 2;
         int TEST_TIME_SECONDS = 10;
         int INTERVAL_SECONDS = 5;
        SlidingTimeWindow stw = new SlidingTimeWindow(INTERVAL_SECONDS);
        ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
        long start = System.currentTimeMillis();
        System.out.println("start" + start);
        for (int i = 0; i < THREAD_POOL_SIZE; i++) {
            executorService.execute(() -> {
                while (System.currentTimeMillis() - start <= TEST_TIME_SECONDS * 1000) {
                    stw.getAndIncrement();
                }
            });
        }

        while (System.currentTimeMillis() - start <= (TEST_TIME_SECONDS+1) * 1000) {
            Thread.sleep(1000);
            System.out.println("waiting");
        }

        executorService.shutdown();
        executorService.awaitTermination(10, TimeUnit.SECONDS);
        int totalCount = 0;
        int expectedCount = stw.get();
        for(int i=0;i<stw.arr.length();i++) {
            totalCount += stw.arr.get(i).getCount().get();
        }


        System.out.println("Total request count: " + totalCount);
        System.out.println("Expected request count: " + expectedCount);
        System.out.println("Difference: " + (expectedCount - totalCount));
        for(int i=1;i<INTERVAL_SECONDS;i++) {
            TimeUnit.SECONDS.sleep(1);
            System.out.println("current:" + stw.get());
        }
    }
}

欢迎大家review代码,多提提意见

标签:java,import,interval,util,concurrent,线程,currentCycle,滑动,工具
From: https://www.cnblogs.com/wsss/p/17385369.html

相关文章

  • 【0基础学爬虫】爬虫基础之自动化工具 Pyppeteer 的使用
    大数据时代,各行各业对数据采集的需求日益增多,网络爬虫的运用也更为广泛,越来越多的人开始学习网络爬虫这项技术,K哥爬虫此前已经推出不少爬虫进阶、逆向相关文章,为实现从易到难全方位覆盖,特设【0基础学爬虫】专栏,帮助小白快速入门爬虫,本期为自动化工具Pyppeteer的使用。概述......
  • 一个C#开发的Windows远程桌面工具
    作为一名程序员,日常远程到服务器再正常不过了,在Windows环境,我们一般是通过操作系统自带、或者第三方工具。今天给你推荐一个开源的Windows远程桌面工具。项目简介这是一个基于MSTSC连接Windows远程桌面,并对其进行封装实现管理多个远程桌面配置的小工具,兼容WindowsXP及以......
  • 多连接的数据库管理工具Navicat Premium 16.1.9 Mac版
    NavicatPremium是一款多连接的数据库管理工具,它是一款免费的多通道、多连接程序,它支持企业和组织同时使用多个应用程序,在一个应用程序中运行多个数据库管理程序。使用Premium可以在同一应用程序中执行多个数据库程序。NavicatPremium可根据应用程序或Web服务之间的速度差异调......
  • 目前常用的在线格式化工具
    一、BeJson格式化工具网址:在线JSON校验格式化工具(BeJSON)优点:工具多缺点:广告多,界面设计较旧,拼凑的工具网站,界面风格差异较大不统一。    二、Robots2开发工具箱网址:Robots2开发工具网站优点:工具界面风格统一,界面整洁,有日常开发用到的工具和网站导航......
  • WPF知识点全攻略15- 线程处理
    使用WPF构建应用程序时,想要保证系统的流畅性、用户的体验性,处理好UI线程(主线程)与其他线程(子线程)的关系是必要的。以最近大火的直播带货为例,镜头前主播(部分副播)的语言动作是主线程,镜头外的场控、客服等人员,各自都有一个属于自己的子线程。场控在做软硬件调试、商品上架下架、发优......
  • 人大金仓数据库迁移工具web版访问方式
    1.新版使用谷歌浏览器进行访问访问地址: http://localhost:54523/默认用户名及密码:kingbase/kingbase2. 老版使用谷歌浏览器进行访问访问地址:http://localhost:8080/默认用户名及密码:admin/123456&*会话保存策略:会话保存时间为一天,服务重启或登出失......
  • HBuilderX启动微信开发者工具报错[error] Error: Fail to open IDE
    报错提示如下: 解决方法:1.使用自己的账号登录。2.在微信开发者平台上申请appid并更改项目中的appid。3.删除项目中微信小程序的appid,这样就能在HBuilderX中启动游客身份的微信开发者工具。  检查其他步骤是否正确:1.打开微信开发者工具,在安全选项里开启服务端口......
  • API文档工具
    SpringBoot实战电商项目mall(50k+star)地址:https://github.com/macrozheng/mallSwaggerSwagger是一款非常流行的API文档工具,它能帮助你简化API文档的开发,极大提高开发效率,之前在mall项目中就是使用的它。  我们一般将Swagger和SpringBoot结合使用,使用的是Springfox给我们提......
  • 最新版本Camera Raw 15.3增效工具,新增AI功能
    Ps关于CameraRaw滤镜的消息大家都听了很多很多了,今天给大家分享的就是CameraRaw的最新版本,也就是那个传说中增加了AI功能的版本。对比先前两个版本,15.3在功能上也就做了2个值得关注的更新:1.AI降噪;2.AI智能蒙板。而改动最大的就是蒙版的支持,目前来看,多个AI蒙版的选择和使......
  • C#里有哪些线程同步的办法
    除了lock和Semaphore之外,C#还有其他的线程同步方法,如Monitor,Mutex,ReaderWriterLockSlim和ManualResetEvent等。目录在常见的编程语言中,同步原语可以分类为哪些?关于锁的名词解释C#里有哪些线程同步的办法lockSemaphoreMonitorMutexReaderWriterLockSlimManualResetEvent......