概述
在 Java 8 引入的新时间 API 中,java.time.Duration 类是处理 时间间隔 的核心工具。它专门用于测量基于时间的时间量(小时、分钟、秒、纳秒),提供了高精度的计算能力和丰富的操作方法。
核心特性
✅ Duration 的独特优势
- 纳秒级精度:最高支持纳秒级别的时间精度
- 不可变线程安全:所有实例都是不可变的,线程安全
- 流畅API设计:提供链式调用的流畅接口
- 单位转换便捷:支持各种时间单位间的轻松转换
- ISO-8601标准兼容:支持标准时间间隔格式
创建 Duration 实例
1. 静态工厂方法
java
// 基本单位创建
Duration ofSeconds = Duration.ofSeconds(30); // 30秒
Duration ofMinutes = Duration.ofMinutes(15); // 15分钟
Duration ofHours = Duration.ofHours(2); // 2小时
Duration ofMillis = Duration.ofMillis(500); // 500毫秒
Duration ofNanos = Duration.ofNanos(1000000); // 1毫秒(100万纳秒)
// 组合创建
Duration complex = Duration.ofHours(1)
.plusMinutes(30)
.plusSeconds(45); // 1小时30分45秒
2. 时间点差值计算
java
// 基于 Instant(时间戳)
Instant startInstant = Instant.now();
Thread.sleep(2000); // 模拟操作
Instant endInstant = Instant.now();
Duration elapsed = Duration.between(startInstant, endInstant);
// 基于 LocalDateTime(无时区)
LocalDateTime meetingStart = LocalDateTime.of(2023, 10, 1, 9, 0);
LocalDateTime meetingEnd = LocalDateTime.of(2023, 10, 1, 11, 30);
Duration meetingDuration = Duration.between(meetingStart, meetingEnd);
// 基于 LocalTime(仅时间)
LocalTime startTime = LocalTime.of(9, 0);
LocalTime endTime = LocalTime.of(17, 30);
Duration workDuration = Duration.between(startTime, endTime);
3. 字符串解析(ISO-8601标准)
java
// ISO-8601 格式解析
Duration pt20s = Duration.parse("PT20S"); // 20秒
Duration pt15m = Duration.parse("PT15M"); // 15分钟
Duration pt10h = Duration.parse("PT10H"); // 10小时
Duration p2d = Duration.parse("P2D"); // 2天
Duration pt1h30m = Duration.parse("PT1H30M"); // 1小时30分钟
核心操作指南
🔢 时间值获取与转换
java
Duration duration = Duration.ofHours(2).plusMinutes(30); // 2小时30分钟
// 转换为不同单位
long totalHours = duration.toHours(); // 2小时
long totalMinutes = duration.toMinutes(); // 150分钟
long totalSeconds = duration.toSeconds(); // 9000秒
long totalMillis = duration.toMillis(); // 9,000,000毫秒
// 获取各部分(不转换单位)
long hoursPart = duration.toHoursPart(); // 2小时
long minutesPart = duration.toMinutesPart(); // 30分钟
long secondsPart = duration.toSecondsPart(); // 0秒
➕ 数学运算操作
java
Duration base = Duration.ofHours(1);
// 加法运算
Duration afterAdd = base.plusHours(2) // 加2小时
.plusMinutes(30) // 加30分钟
.plus(Duration.ofSeconds(10)); // 加10秒
// 减法运算
Duration afterSubtract = base.minusMinutes(15)
.minus(Duration.ofSeconds(30));
// 乘除运算
Duration doubled = base.multipliedBy(2); // 乘以2
Duration halved = base.dividedBy(2); // 除以2
// 取绝对值
Duration negative = Duration.ofHours(-1);
Duration absolute = negative.abs(); // 1小时
⚖️ 比较与判断
java
Duration shortTask = Duration.ofMinutes(5);
Duration longTask = Duration.ofHours(1);
// 比较操作
boolean isShorter = shortTask.compareTo(longTask) < 0; // true
boolean isZero = shortTask.isZero(); // false
boolean isNegative = shortTask.isNegative(); // false
// 相等判断
boolean equal = shortTask.equals(Duration.ofMinutes(5)); // true
实战应用场景
🎯 场景1:性能监控与执行时间测量
java
public class PerformanceMonitor {
public static <T> T measureExecution(String taskName,
Supplier<T> task) {
Instant start = Instant.now();
try {
T result = task.get();
Instant end = Instant.now();
Duration duration = Duration.between(start, end);
System.out.printf("任务 '%s' 执行时间: %d 毫秒%n",
taskName, duration.toMillis());
return result;
} catch (Exception e) {
Instant end = Instant.now();
Duration duration = Duration.between(start, end);
System.err.printf("任务 '%s' 失败,已执行: %d 毫秒%n",
taskName, duration.toMillis());
throw e;
}
}
// 使用示例
public static void main(String[] args) {
String result = measureExecution("数据查询", () -> {
// 模拟耗时操作
try { Thread.sleep(1500); } catch (InterruptedException e) {}
return "查询结果";
});
}
}
🎯 场景2:超时控制与限流管理
java
public class TimeoutManager {
private final Duration timeout;
private final Duration retryInterval;
public TimeoutManager(Duration timeout, Duration retryInterval) {
this.timeout = timeout;
this.retryInterval = retryInterval;
}
public <T> T executeWithTimeout(Callable<T> task) throws TimeoutException {
Instant deadline = Instant.now().plus(timeout);
while (Instant.now().isBefore(deadline)) {
try {
return task.call();
} catch (Exception e) {
Duration remaining = Duration.between(Instant.now(), deadline);
if (remaining.toMillis() <= 0) {
throw new TimeoutException("操作超时");
}
// 等待后重试
try {
Thread.sleep(Math.min(retryInterval.toMillis(),
remaining.toMillis()));
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException("操作被中断", ie);
}
}
}
throw new TimeoutException("操作超时");
}
}
// 使用示例
TimeoutManager manager = new TimeoutManager(
Duration.ofSeconds(30), // 30秒超时
Duration.ofSeconds(2) // 2秒重试间隔
);
🎯 场景3:缓存过期策略实现
java
public class TimedCache<K, V> {
private final Map<K, CacheEntry<V>> cache = new ConcurrentHashMap<>();
private final Duration defaultTtl;
public TimedCache(Duration defaultTtl) {
this.defaultTtl = defaultTtl;
}
public void put(K key, V value) {
put(key, value, defaultTtl);
}
public void put(K key, V value, Duration ttl) {
Instant expiryTime = Instant.now().plus(ttl);
cache.put(key, new CacheEntry<>(value, expiryTime));
}
public V get(K key) {
CacheEntry<V> entry = cache.get(key);
if (entry == null) return null;
if (Instant.now().isAfter(entry.getExpiryTime())) {
cache.remove(key);
return null;
}
return entry.getValue();
}
// 清理过期条目
public void cleanup() {
Instant now = Instant.now();
cache.entrySet().removeIf(entry ->
now.isAfter(entry.getValue().getExpiryTime()));
}
private static class CacheEntry<V> {
private final V value;
private final Instant expiryTime;
public CacheEntry(V value, Instant expiryTime) {
this.value = value;
this.expiryTime = expiryTime;
}
// getters...
}
}
🎯 场景4:任务调度与延迟执行
java
public class TaskScheduler {
private final ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(2);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task,
Duration initialDelay,
Duration delay) {
return scheduler.scheduleWithFixedDelay(
task,
initialDelay.toMillis(),
delay.toMillis(),
TimeUnit.MILLISECONDS
);
}
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task,
Duration initialDelay,
Duration period) {
return scheduler.scheduleAtFixedRate(
task,
initialDelay.toMillis(),
period.toMillis(),
TimeUnit.MILLISECONDS
);
}
// 使用示例
public void startMonitoring() {
// 5秒后开始,每30秒执行一次
scheduleAtFixedRate(
this::healthCheck,
Duration.ofSeconds(5),
Duration.ofSeconds(30)
);
}
private void healthCheck() {
System.out.println("执行健康检查: " + Instant.now());
}
}
🎯 场景5:速率限制器实现
java
public class RateLimiter {
private final Duration refillInterval;
private final int maxTokens;
private int tokens;
private Instant lastRefill;
public RateLimiter(Duration refillInterval, int maxTokens) {
this.refillInterval = refillInterval;
this.maxTokens = maxTokens;
this.tokens = maxTokens;
this.lastRefill = Instant.now();
}
public synchronized boolean tryAcquire() {
refillTokens();
if (tokens > 0) {
tokens--;
return true;
}
return false;
}
public synchronized boolean tryAcquire(int permits) {
refillTokens();
if (tokens >= permits) {
tokens -= permits;
return true;
}
return false;
}
private void refillTokens() {
Instant now = Instant.now();
Duration timeSinceLastRefill = Duration.between(lastRefill, now);
long refillCount = timeSinceLastRefill.dividedBy(refillInterval);
if (refillCount > 0) {
tokens = Math.min(maxTokens, tokens + (int)refillCount);
lastRefill = lastRefill.plus(refillInterval.multipliedBy(refillCount));
}
}
public Duration getTimeUntilNextRefill() {
Instant nextRefill = lastRefill.plus(refillInterval);
return Duration.between(Instant.now(), nextRefill);
}
}
// 使用示例:限制每秒最多10个请求
RateLimiter limiter = new RateLimiter(Duration.ofSeconds(1), 10);
if (limiter.tryAcquire()) {
// 处理请求
} else {
Duration waitTime = limiter.getTimeUntilNextRefill();
System.out.println("需要等待: " + waitTime.toMillis() + "毫秒");
}
最佳实践与注意事项
✅ 最佳实践
java
// 1. 使用有意义的变量名
Duration requestTimeout = Duration.ofSeconds(30);
Duration cacheTtl = Duration.ofMinutes(10);
Duration sessionDuration = Duration.ofHours(2);
// 2. 优先使用工厂方法而不是构造函数
Duration good = Duration.ofMinutes(5); // ✅ 推荐
Duration bad = Duration.ofSeconds(300); // ❌ 可读性差
// 3. 合理处理大数值
Duration largeDuration = Duration.ofDays(365); // 1年
if (largeDuration.toDays() > 100) {
// 处理大时间间隔
}
// 4. 使用合适的精度
Duration highPrecision = Duration.ofNanos(100); // 需要高精度时
Duration normalPrecision = Duration.ofMillis(100); // 一般情况
⚠️ 注意事项
java
// 1. 避免在日期计算中使用 Duration(使用 Period)
LocalDate today = LocalDate.now();
LocalDate nextMonth = today.plus(Period.ofMonths(1)); // ✅ 正确
LocalDate wrong = today.plus(Duration.ofDays(30)); // ❌ 可能不准确
// 2. 注意时区问题
ZonedDateTime zonedStart = ZonedDateTime.now();
ZonedDateTime zonedEnd = zonedStart.plus(Duration.ofHours(2));
Duration duration = Duration.between(zonedStart, zonedEnd); // ✅ 正确处理时区
// 3. 大数值计算可能溢出
Duration veryLarge = Duration.ofDays(Integer.MAX_VALUE);
long days = veryLarge.toDays(); // 可能溢出,需要检查
总结
Duration 是以下场景的理想选择:
- ✅ 高精度时间测量(性能监控、执行时间统计)
- ✅ 超时控制和限流(网络请求、资源管理)
- ✅ 缓存和会话管理(TTL设置、过期策略)
- ✅ 任务调度(延迟执行、定期任务)
- ✅ 速率限制(API限流、流量控制)
关键优势:
- 纳秒级精度满足高性能需求
- 不可变设计保证线程安全
- 流畅API提升开发体验
- 丰富的时间单位转换支持
掌握 Duration 的使用,能够让你在时间敏感的应用场景中游刃有余,构建出更加健壮和高效的系统。