Google Guava之RateLimiter核心源码解读(上)
程序员文章站
2022-03-10 16:03:32
RateLimiter是Google Guava框架的一个限速器,通常用于控制对某个资源的访问速率。限速常见的有两种实现方式,一种是令牌桶,另一种是漏桶。RateLimiter选择了令牌桶作为其底层实现,按照固定速率投放令牌,同时支持突发流量。本篇先来解读一下Ticker和Stopwatch,它们是RateLimiter底层时间计算的基础。代码基于Guava 23.0版本。TickerTicker的意思钟表,里面只有一个read()方法,用来获取当前时间。核心代码解读:public abst...
RateLimiter是Google Guava框架的一个限速器,通常用于控制对某个资源的访问速率。
限速常见的有两种实现方式,一种是令牌桶,另一种是漏桶。
RateLimiter选择了令牌桶作为其底层实现,按照固定速率投放令牌,同时支持突发流量。
本篇先来解读一下Ticker和Stopwatch,它们是RateLimiter底层时间计算的基础。
代码基于Guava 23.0版本。
Ticker
Ticker的意思钟表,里面只有一个read()方法,用来获取当前时间。
核心代码解读:
public abstract class Ticker {
// 获取当前时间
public abstract long read();
// Ticker默认实现. read()内部实际是调用java.lang.System.nanoTime()方法
private static final Ticker SYSTEM_TICKER =
new Ticker() {
@Override
public long read() {
return Platform.systemNanoTime();
}
};
}
Stopwatch
Stopwatch是一个计时器,也叫秒表。通常用于测量程序功能的运行时长。
功能和实际的物理秒表非常相似,现在手机一般都会有秒表功能,大家可以先用手机感受一下,然后再去理解该类就会比较简单了。
秒表通常会支持开始、停止和重置。可以将多次开始和结束之间的时间进行累计,支持重置秒表后重新开始。没错,Stopwatch就实现了这个最基本的功能。
核心代码解读:
public final class Stopwatch {
/**
* 钟表. 主要用于获取当前的纳秒数,默认使用System.nanoTime().
*/
private final Ticker ticker;
/**
* 秒表的运行状态.<br>
* 调用start()时设置为true,表示运行中,开始计时.<br>
* 调用stop()、reset()时设置为false,表示停止,结束本次计时.
*/
private boolean isRunning;
/**
* 秒表总的计时时长.<br>
* 计算规则:每执行一次"start() 到 stop()"之间经过的时间之和。从"stop() 到 start()"之间的时间不做累计.<br>
* 更新时机:仅在执行stop()和reset()时更新.
*/
private long elapsedNanos;
/**
* 秒表开始计时的时间. 每次调用start()时更新为当前时间,进行下一轮的计时.
*/
private long startTick;
/**
* 默认构造函数. 使用默认的Ticker
*/
Stopwatch() {
this.ticker = Ticker.systemTicker();
}
/**
* 支持自定义Ticker的构造函数. 例如对于Android,Ticker可以基于android.os.SystemClock.elapsedRealtimeNanos()来实现.
*
* @param ticker
*/
Stopwatch(Ticker ticker) {
this.ticker = checkNotNull(ticker, "ticker");
}
/**
* 启动秒表
*/
@CanIgnoreReturnValue
public Stopwatch start() {
checkState(!isRunning, "This stopwatch is already running.");
isRunning = true; // 状态设置为运行中
startTick = ticker.read(); // 保存计时开始时间
return this;
}
/**
* 停止秒表
*/
@CanIgnoreReturnValue
public Stopwatch stop() {
long tick = ticker.read(); // 获取当前时间
checkState(isRunning, "This stopwatch is already stopped.");
isRunning = false; // 状态设置为停止运行
elapsedNanos += tick - startTick; // 将本次"start() 到 stop()"之间的计时时长累计到elapsedNanos中
return this;
}
/**
* 获取秒表总的计时时长. 实际上,就是返回截至当前时间为止的elapsedNanos.<br>
* 如果秒表还在计时中,elapsedNanos需要累计"本次计时到当前时间为止"的耗时,因此返回:ticker.read() - startTick + elapsedNanos.<br>
* 如果秒表已经停止了,那么直接返回elapsedNanos。因为如果已经调用了stop(),则已经执行了一次:elapsedNanos += ticker.read() - startTick.
*
* @return
*/
private long elapsedNanos() {
return isRunning ? ticker.read() - startTick + elapsedNanos : elapsedNanos;
}
/**
* 重置秒表
*/
@CanIgnoreReturnValue
public Stopwatch reset() {
elapsedNanos = 0; // 计时清0
isRunning = false; // 设置状态为停止
return this;
}
}
示例代码
最后,再提供一个例子方便大家测试。
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import com.google.common.base.Stopwatch;
public class StopwatchTest {
@Test
public void testStopwatch() {
Stopwatch watch = Stopwatch.createStarted();
System.out.println(watch.elapsed(TimeUnit.MILLISECONDS));
for (int i = 0; i < 5; i++) {
sleep(500);
System.out.println(watch.elapsed(TimeUnit.MILLISECONDS));
}
}
@Test
public void testStopwatchAndStop() {
Stopwatch watch = Stopwatch.createStarted();
System.out.println("开始时间:" + watch.elapsed(TimeUnit.MILLISECONDS));
for (int i = 0; i < 2; i++) {
sleep(100);
System.out.println("100ms后,实时计算elapsedNanos:" + watch.elapsed(TimeUnit.MILLISECONDS)); // 实时计算elapsedNanos
sleep(400);
watch.stop(); // 更新elapsedNanos. 将本次"start() 到 stop()"之间的耗时累加到elapsedNanos.
System.out.println("400ms后,直接返回elapsedNanos:" + watch.elapsed(TimeUnit.MILLISECONDS)); // 调用stop()已经计算好了elapsedNanos
// doSometing(). stop()与start()之间的时间不会计算在内,忽略
sleep(500);
System.out.println("stop()之后等待500ms,elapsedNanos与上次相同:" + watch.elapsed(TimeUnit.MILLISECONDS)); // 调用stop()已经计算好了elapsedNanos
watch.start(); // 更新startTick为当前时间
}
}
private void sleep(long millis) {
try {
TimeUnit.MILLISECONDS.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
testStopwatchAndStop()运行结果:
开始时间:0
100ms后,实时计算elapsedNanos:99
400ms后,直接返回elapsedNanos:499
stop()之后等待500ms,elapsedNanos与上次相同:499
100ms后,实时计算elapsedNanos:599
400ms后,直接返回elapsedNanos:999
stop()之后等待500ms,elapsedNanos与上次相同:999
—转载本站文章请注明作者和出处 二进制之路(binarylife.icu),请勿用于任何商业用途—
公众号:二进制之路
本文地址:https://blog.csdn.net/zhanjia/article/details/108806656
上一篇: 线程同步和线程死锁
下一篇: webpack中常用的loader