应用背景:
游戏抽奖,控制抽奖命中的最大次数,当抽奖命中次数达到上限,再继续抽奖无法出现命中情况;控制抽奖命中最低次数,确保流逝次数范围内至少命中次数。
package com.yx.demo.rate;
/**
* RateLimiter
* 比率限定器,把比率限定在指定范围内。 <p/>
* <p>
* 比率限定器必须要制定perCount和maxLimit,即每perCount最多发生maxLimit,比如
* 每10000次最多发生3次,minLimit可选,是发生的下限。<p/>
* <p>
* 比率限定器不包含随机方法,使用比率限定器需手动调用elapse(流逝)方法来增加
* 当前的流逝次数,比如1000此的流逝到第500次。使用isUnderLimit方法获取是否仍然
* 在比率限定中,如果在比率限定中,那么返回true,并且增加一次计数,大于等于
* 最高限制时,isUnderLimit将返回false
*
* @author yx
* @date 2019/12/20 23:40
*/
public class RateLimiter {
/**
* 每次数量,每次这个数量下限制多少次的成功数
*/
private int perCount;
/**
* 最高限制
*/
private int maxLimit;
/**
* 最低限制,默认0
*/
private int minLimit = 0;
/**
* 当前累计经过数量,已经流逝的次数
*/
private int currentCount = 0;
/**
* 当前累计赢得数量,已经成功的次数
*/
private int currentWinCount = 0;
/**
* 当前限制量,可能会大于maxLimit
*/
private int currentLimit = 0;
/**
* 默认构造函数。
*/
public RateLimiter() {
}
/**
* 带有构造函数的比率限定器。
*
* @param perCount 每次数量
* @param maxLimit 最高数量
* @param minLimit 最小数量
*/
public RateLimiter(int perCount, int maxLimit, int minLimit) {
this.perCount = perCount;
this.maxLimit = maxLimit;
this.minLimit = minLimit;
this.currentLimit = this.maxLimit;
}
/**
* 流逝一次次数。
*/
public synchronized void elapse() {
currentCount++;
if (currentCount >= perCount) {
// 把上次间隔的未达到最低概率的移到以后,让
// 后来者有更高的机会,从而保证最低数
int lastUnUsed = minLimit - currentWinCount;
if (lastUnUsed < 0) {
// 不能为负,否则将会减少以后的机会
lastUnUsed = 0;
}
currentLimit = lastUnUsed + maxLimit;
currentCount = 0;
// 重置当前的赢的次数
currentWinCount = 0;
}
}
/**
* 赢的一次。
*/
public synchronized void win() {
currentWinCount++;
currentLimit--;
assert (currentLimit >= 0);
}
/**
* 是否当前还在指定的比率限定中,此方法不会增加流逝次数。
*
* @return 是否还在比率限定中
*/
public synchronized boolean isUnderLimit() {
if (currentWinCount < currentLimit) {
return true;
} else {
return false;
}
}
public int getPerCount() {
return perCount;
}
public void setPerCount(int perCount) {
this.perCount = perCount;
}
public int getMaxLimit() {
return maxLimit;
}
public void setMaxLimit(int maxLimit) {
this.maxLimit = maxLimit;
if (this.currentLimit == 0) {
this.currentLimit = this.maxLimit;
}
}
public int getMinLimit() {
return minLimit;
}
public void setMinLimit(int minLimit) {
this.minLimit = minLimit;
if (this.currentLimit == 0) {
this.currentLimit = this.minLimit;
}
}
public int getCurrentCount() {
return currentCount;
}
public void setCurrentCount(int currentCount) {
this.currentCount = currentCount;
}
public int getCurrentWinCount() {
return currentWinCount;
}
public void setCurrentWinCount(int currentWinCount) {
this.currentWinCount = currentWinCount;
}
public int getCurrentLimit() {
return currentLimit;
}
}