importjava.io.Serializable;importjava.util.ArrayList;importjava.util.Collections;importjava.util.HashMap;importjava.util.LinkedHashMap;importjava.util.List;importjava.util.Map;importjava.util.concurrent.CountDownLatch;importjava.util.concurrent.TimeUnit;importjava.util.concurrent.atomic.LongAdder;importjava.util.concurrent.locks.ReentrantLock;/*** @Author: gxj*/
public classQPSCalculator {privateRollingNumber rollingNumber;publicQPSCalculator() {this.rollingNumber = newRollingNumber();
}public voidpass() {
rollingNumber.record();
}private static final classRollingNumber {/*** 槽位的数量*/
private intsizeOfBuckets;/*** 时间片,单位毫秒*/
private intunitOfTimeSlice;/*** 用于判断是否可跳过锁争抢*/
private inttimeSliceUsedToCheckIfPossibleToBypass;/*** 槽位*/
privateBucket[] buckets;/*** 目标槽位的位置*/
private volatileInteger targetBucketPosition;/*** 接近目标槽位最新更新时间的时间*/
private volatileLong latestPassedTimeCloseToTargetBucket;/*** 进入下一个槽位时使用的锁*/
privateReentrantLock enterNextBucketLock;/*** 默认60个槽位,槽位的时间片为1000毫秒*/
publicRollingNumber() {this(60, 1000);
}/*** 初始化Bucket数量与每个Bucket的时间片等
*
*@paramsizeOfBuckets
*@paramunitOfTimeSlice*/
public RollingNumber(int sizeOfBuckets, intunitOfTimeSlice) {this.latestPassedTimeCloseToTargetBucket = System.currentTimeMillis() - (2 *unitOfTimeSlice);this.targetBucketPosition = null;this.sizeOfBuckets =sizeOfBuckets;this.unitOfTimeSlice =unitOfTimeSlice;this.enterNextBucketLock = newReentrantLock();this.buckets = newBucket[sizeOfBuckets];this.timeSliceUsedToCheckIfPossibleToBypass = 3 *unitOfTimeSlice;for (int i = 0; i < sizeOfBuckets; i++) {this.buckets[i] = newBucket();
}
}private voidrecord() {long passTime =System.currentTimeMillis();if (targetBucketPosition == null) {
targetBucketPosition= (int) (passTime / unitOfTimeSlice) %sizeOfBuckets;
}
Bucket currentBucket=buckets[targetBucketPosition];if (passTime - latestPassedTimeCloseToTargetBucket >=unitOfTimeSlice) {if (enterNextBucketLock.isLocked() && (passTime - latestPassedTimeCloseToTargetBucket)
}else{try{
enterNextBucketLock.lock();if (passTime - latestPassedTimeCloseToTargetBucket >=unitOfTimeSlice) {int nextTargetBucketPosition = (int) (passTime / unitOfTimeSlice) %sizeOfBuckets;
Bucket nextBucket=buckets[nextTargetBucketPosition];if(nextBucket.equals(currentBucket)) {if (passTime - latestPassedTimeCloseToTargetBucket >=unitOfTimeSlice) {
latestPassedTimeCloseToTargetBucket=passTime;
}
}else{
nextBucket.reset(passTime);
targetBucketPosition=nextTargetBucketPosition;
latestPassedTimeCloseToTargetBucket=passTime;
}
nextBucket.pass();return;
}else{
currentBucket=buckets[targetBucketPosition];
}
}finally{
enterNextBucketLock.unlock();
}
}
}
currentBucket.pass();
}publicBucket[] getBuckets() {returnbuckets;
}
}private static class Bucket implementsSerializable {private static final long serialVersionUID = -9085720164508215774L;privateLong latestPassedTime;privateLongAdder longAdder;publicBucket() {this.latestPassedTime =System.currentTimeMillis();this.longAdder = newLongAdder();
}public voidpass() {
longAdder.add(1);
}public longcountTotalPassed() {returnlongAdder.sum();
}public longgetLatestPassedTime() {returnlatestPassedTime;
}public void reset(longlatestPassedTime) {this.longAdder.reset();this.latestPassedTime =latestPassedTime;
}
}public static voidmain(String[] args) {try{final QPSCalculator qpsCalculator = newQPSCalculator();int threadNum = 4;
CountDownLatch countDownLatch= newCountDownLatch(threadNum);
List threadList = new ArrayList();for (int i = 0; i < threadNum; i++) {
threadList.add(newThread() {public voidrun() {for (int i = 0; i < 50000000; i++) {
qpsCalculator.pass();
}
countDownLatch.countDown();
}
});
}long startTime =System.currentTimeMillis();for(Thread thread : threadList) {
thread.start();
}
countDownLatch.await();long endTime =System.currentTimeMillis();long totalTime = endTime -startTime;
System.out.print("totalMilliseconds: " +totalTime);
TimeUnit.SECONDS.sleep(1000L);
}catch(Exception e) {
e.printStackTrace();
}
}
}