滑动窗口计数的java实现-循环数组
一、用循环数组实现滑动窗口
1.1、实现思想
1.定义一个AtomicInteger array数组,每一个元素记录当前区间的计数
2.定义一个long数组 times,记录对应array下标元素开始的时间.
3.定义一个下标int index 记录当前正在使用的位置.
4.定义每个元素的时间区间大小span = 200 ms
index变化情况如下:
1、如果当前时间now - times[index]>span 说明当前请求计数应当位于下一个位置的元素.
index++,
如果index>=size(当前数组大小),则index=0;
2、如果now-times[index]大于传入的计数时间 如1s,则说明,该时间元素无效,重置:
times[index]=now;
array[index].set(0);
1.2、计数逻辑
1.加锁控制
2.按照上面index逻辑更新index,和对应array和times数组里面的元素信息
1.3、获取当前1s内数量
如果get方法加锁,则会影响滑动窗口性能,所以该方法不加锁,得到的值是一个近似精确的值.
实现逻辑:
1.获取到当前下标curIndex
2.设定循环次数: time = seconds(滑动窗口统计时间) * 1000 /span
3、开始循环,递减curIndex累加当前array对应的值,
判断条件
1.当前index 对应的times[index] 符合now-times[index]<seconds *1000,因为如果大于就认为超过该seconds统计区间
2、不超过time次循环
二、代码实现
链接:
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
public class ArraySideWindow {
private final AtomicInteger[] array ;
private final long[] times;
private final int span=200;
private final int size ;
private final int delta = 5;
private final int MILL=1000;
private final int second;
private volatile int index;
private ReentrantLock lock;
public ArraySideWindow(int second){
this.second = second;
this.size=second* MILL / span + delta;
this.array = new AtomicInteger[size];
this.times = new long[size];
this.index=0;
for(int i=0;i<size;i++){
this.array[i]=new AtomicInteger(0);
}
this.times[index]=System.currentTimeMillis();
this.lock = new ReentrantLock(false);
}
public void count(){
long now = System.currentTimeMillis();
lock.lock();
try{
if(now-times[index]>span){
index++;
index=index<size?index:0;
}
if(now-times[index]>=second * MILL){
times[index]=now;
this.array[index].set(0);
}
this.array[index].incrementAndGet();
//测试打印 忽略
printNum(this.array);
}catch (Exception e){
}finally {
lock.unlock();
}
}
public int get(){
int curIndex = index;
long now = System.currentTimeMillis();
int count=0;
int sum=0;
while (count<5){
if(now-times[curIndex]>=second * MILL){
break;
}
sum +=array[curIndex--].get();
if(curIndex<0){
curIndex=size-1;
}
count++;
}
return sum;
}
/**
* 测试代码
* @param array
*/
private synchronized void printNum(AtomicInteger[] array) {
Random random = new Random();
int r = random.nextInt(10);
if(r>=3){
return;
}
StringBuilder numBuilder = new StringBuilder();
StringBuilder timeBuilder = new StringBuilder();
for(int i=0;i<array.length;i++){
numBuilder.append(array[i].get()).append(",");
timeBuilder.append(times[i]).append(",");
}
System.out.println("-----------------------------------------");
int preIndex = index-1<0?size-1: index-1;
System.out.println("now:"+System.currentTimeMillis()+",curIndex:"+index+",preIndex:"+preIndex);
System.out.println("indexTime:"+times[index]+",preIndexTime:"+times[preIndex]+",dif:"+(times[index]-times[preIndex]));
System.out.println("Thread:"+Thread.currentThread().getName());
System.out.println(numBuilder.toString());
System.out.println(timeBuilder.toString());
System.out.println("-----------------------------------------");
}
/**
* 测试
* @param args
*/
public static void main(String[] args) {
ArraySideWindow window = new ArraySideWindow(1);
AtomicInteger sum =new AtomicInteger(0);
Random random = new Random(10);
for(int i=0;i<30;i++){
Thread t = new Thread(()->{
int time = 0;
while (time<100000){
int sumNow = sum.get();
if(sumNow>10000){
System.out.println("**************************************************");
try {
TimeUnit.MILLISECONDS.sleep(400+random.nextInt(30));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
window.count();
try {
TimeUnit.MILLISECONDS.sleep(2+random.nextInt(30));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread:"+Thread.currentThread().getName()+",num:"+window.get());
sum.incrementAndGet();
}
},"thread_"+i);
t.start();
}
try {
TimeUnit.SECONDS.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
java基础
Spring ApplicationContext启动方式
程序猿老徐阅读 472
Java8的新特性
codecraft赞 32阅读 27.4k评论 1
一文彻底搞懂加密、数字签名和数字证书!
编程指北赞 71阅读 33.5k评论 20
Java11的新特性
codecraft赞 28阅读 19.3k评论 3
Java5的新特性
codecraft赞 13阅读 21.7k
Java9的新特性
codecraft赞 20阅读 15.3k
Java13的新特性
codecraft赞 17阅读 11.1k
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。