fast fast fast
場景
業(yè)務(wù)中經(jīng)常需要判斷傳入的時間是不是今天徘熔,之前封裝有一個方法
public static boolean isToday(long timeMillis) {
LocalDate now = LocalDate.now();
LocalDate fromDate = Instant.ofEpochMilli(timeMillis).atZone(ZoneId.systemDefault()).toLocalDate();
return now.equals(fromDate);
}
基于LocalDate比較,具體思路是將時間戳轉(zhuǎn)換成LocalDate跪另,再基于LocalDate進行比較倔约。由于LocalDate內(nèi)部有很多隱藏邏輯啥刻,且每次調(diào)用這個方法都要創(chuàng)建兩個LocalDate對象,這段代碼只能說是正確但低效奸鸯。
我們需要一種更高效的方式,經(jīng)過一番思考和行動可帽,得出了下面這個方式
基于時間范圍比較
它有兩個特點
- 基于時間范圍判斷
- 利用線程封閉避免競態(tài)
實現(xiàn)利用了Guava的Range工具類娄涩,可以快速判斷某個值是否在給定范圍內(nèi)
public class TimeUtil {
public static final long MILLIS_ONE_DAY = 24 * 60 * 60 * 1000;
/**
* 今日時間范圍,[今日開始時間戳,今日結(jié)束時間戳)
*/
private static final ThreadLocal<Range<Long>> todayTimeRangeCache =
ThreadLocal.withInitial(TimeUtil::createTodayTimeRange);
/**
* 獲取今日0點
*
* @return
*/
public static long getTodayZeroMillis() {
return LocalDateTime.of(LocalDate.now(), LocalTime.MIN).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}
/**
* 傳入時間是否屬于今天
*
* @param timeMillis
* @return
*/
public static boolean isToday(long timeMillis) {
long now = System.currentTimeMillis();
Range<Long> todayTimeRange = todayTimeRangeCache.get();
if (!todayTimeRange.contains(now)) {
Range<Long> timeRange = createTodayTimeRange();
todayTimeRangeCache.set(timeRange);
todayTimeRange = timeRange;
}
return todayTimeRange.contains(timeMillis);
}
private static Range<Long> createTodayTimeRange() {
long todayStartTime = getTodayZeroMillis();
// 如果需要考慮夏令時, 這里需要改為其他方式,邏輯于getTodayZeroMillis類似
long todayEndTime = todayStartTime + MILLIS_ONE_DAY;
return Range.closedOpen(todayStartTime, todayEndTime);
}
}
新舊方案對比
方式 | 基于LocalDate對比 | 基于時間范圍對比 |
---|---|---|
內(nèi)存消耗 | 每次調(diào)用都要創(chuàng)建兩個LocalDate對象,O(n) | 每個線程僅需要創(chuàng)建一個映跟,O(1) |
性能 | 時間戳轉(zhuǎn)換為對象蓄拣,根據(jù)當(dāng)前時間創(chuàng)建對象兜粘,均為復(fù)雜的邏輯 | 獲取一次當(dāng)前時間,兩到三次比較操作 |
isToday是一個高頻調(diào)用的方法弯蚜,對一個長期運行的項目,經(jīng)過優(yōu)化后的方式很有意義剃法。