public class SlidingWindow {
/**
* 循环队列 分段存放数据
*/
private AtomicInteger[] timeSlices;
/**
* 每个时间片的时长,以毫秒为单位
*/
private int timeMillisPerSlice;
/**
* 共有多少个时间片(即窗口长度)
*/
private int windowSize;
/**
* 最近一次计数的时间戳
*/
private volatile long lastAddTimestamp;
/**
* 最近一次个更新的index
*/
private int lastAddIndex;
private SlidingWindow(int timeMillisPerSlice, int windowSize) {
this.timeMillisPerSlice = timeMillisPerSlice;
this.windowSize = windowSize;
lastAddIndex = 0;
//基本校验
if (timeMillisPerSlice < 1) {
throw new RuntimeException("timeMillisPerSlice > 0");
}
if (windowSize < 2) {
throw new RuntimeException("timeMillisPerSlice > 1");
}
//初始化数据
timeSlices = new AtomicInteger[windowSize];
for (int i = 0; i < windowSize; i++) {
timeSlices[i] = new AtomicInteger(0);
}
}
/**
* 增加count个数量
*/
public int addAndSum() {
//当前自己所在的位置,是哪个小时间窗
doAdd();
System.out.println(this.toString());
int sum = geSum();
System.out.println(sum);
return sum;
}
private int geSum() {
int sum = 0;
for (int i = 0; i < windowSize; i++) {
sum += timeSlices[i].get();
}
return sum;
}
/**
* 计算当前所在的时间片的位置
*/
private int doAdd() {
long now = System.currentTimeMillis();
//如果当前的key已经超出一整个时间片了,那么就直接初始化就行了,不用去计算了
if (now - lastAddTimestamp > getTotalWindowTime()) {
reset();
}
int currIndex = (int) ((now / timeMillisPerSlice) % windowSize);
System.out.println("currIndex=" + currIndex + ",lastAddIndex=" + lastAddIndex);
int tempIndex = lastAddIndex;
if(currIndex != tempIndex){
int resetIndex = currIndex;
if(currIndex < tempIndex){
resetIndex += windowSize;
}
System.out.print("清空:");
for (int i = tempIndex + 1; i <= resetIndex; i++) {
System.out.print(i % windowSize + " ");
timeSlices[i % windowSize].set(0);
}
}
tempIndex = currIndex % windowSize;
System.out.println("tempIndex =" + tempIndex);
timeSlices[tempIndex].addAndGet(1);
if(lastAddTimestamp < now){
lastAddIndex = tempIndex;
lastAddTimestamp = now;
}
return tempIndex;
}
private int getTotalWindowTime() {
return timeMillisPerSlice * windowSize;
}
/**
* 初始化
*/
private void reset() {
for (int i = 0; i < windowSize; i++) {
timeSlices[i].set(0);
}
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("SlidingWindow{");
sb.append("timeSlices=").append(timeSlices == null ? "null" : Arrays.asList(timeSlices).toString());
sb.append(", timeMillisPerSlice=").append(timeMillisPerSlice);
sb.append(", windowSize=").append(windowSize);
sb.append(", lastAddIndex=").append(lastAddIndex);
sb.append('}');
return sb.toString();
}
public static void main(String[] args) throws InterruptedException {
SlidingWindow slidingWindow = new SlidingWindow(500, 4);
while (true){
slidingWindow.addAndSum();
Thread.sleep(600);
}
}
滑动窗口
主要通过抽象出几个窗口,在时间流逝(滑动)中进行计数,保证一段时间内的数据新鲜。
构造方法
初始化后的类状态信息:
主逻辑addAndSum()
主要计数 过期 覆盖的主逻辑都在这个方法里