1、前言
Guava:由Google团队开源的Java核心增强库,涵盖集合、并发原语、缓存、IO、反射等多种工具。其性能和稳定性有保障,广泛应用于各类项目中。
2、Get方法源码
java
V get(K key, int hash, CacheLoader<? super K, V> loader) throws ExecutionException {
checkNotNull(key);
checkNotNull(loader);
try {
// count 表示在这个segment中存活的项目个数
if (count != 0) {
// 获取segment中的元素, 包含正在load的数据
ReferenceEntry<K, V> e = getEntry(key, hash);
if (e != null) {
// 获取当前时间,纳秒
long now = map.ticker.read();
// 获取缓存值,判断是否过期,过期删除缓存并返回null
V value = getLiveValue(e, now); // 拿到没过期存活的数据
if (value != null) {
// 记录访问时间
recordRead(e, now);
// 记录缓存命中一次
statsCounter.recordHits(1);
// 刷新缓存并返回缓存值
return scheduleRefresh(e, key, hash, value, now, loader);
}
// 走到这步,说明取出来的值 value == null, 可能是过期了,也有可能正在刷新
ValueReference<K, V> valueReference = e.getValueReference();
// 如果此时value正在loading,那么此时等待刷新结果
if (valueReference.isLoading()) {
return waitForLoadingValue(e, key, valueReference);
}
}
}
// 走到这说明值为null或者过期,需要加锁进行加载
return lockedGetOrLoad(key, hash, loader);
} catch (ExecutionException ee) {
Throwable cause = ee.getCause();
if (cause instanceof Error) {
throw new ExecutionError((Error) cause);
} else if (cause instanceof RuntimeException) {
throw new UncheckedExecutionException(cause);
}
throw ee;
} finally {
postReadCleanup();
}
}
3、最佳实践
3.1、防御性编程:参数校验
scss
java
checkNotNull(key); // 防御空指针
checkNotNull(loader);
借鉴点:
-
业务场景:任何方法的入口参数校验(如订单ID不能为空)
-
实现方式:
lessjava // Spring 风格示例 public Order getOrder(@NotNull String orderId) { Assert.notNull(orderId, "Order ID must not be null"); // ... }
-
价值:避免后续逻辑因空值崩溃,提前暴露问题。
3.2、分段锁优化并发
scss
java
lockedGetOrLoad(key, hash, loader); // 分段锁控制
借鉴点:
-
业务场景:高并发访问共享资源(如库存扣减、配置热更新)
-
实现方式:
csharpjava // 使用 ConcurrentHashMap 的分段锁思想 private final Striped<Lock> locks = Striped.lock(32); // 分32个锁段 public void updateProductStock(String productId) { Lock lock = locks.get(productId.hashCode()); lock.lock(); try { // 更新库存... } finally { lock.unlock(); } }
-
价值:减少锁竞争,提升并发吞吐量。
3.3、双重检查锁定(DCL)
scss
java
if (count != 0) { // 第一次检查(无锁)
e = getEntry(key, hash);
if (e != null) { // 第二次检查(加锁后)
// ...
}
}
借鉴点:
-
业务场景:单例模式、缓存加载(如全局配置初始化)
-
实现方式:
arduinojava public class ConfigLoader { private volatile Config config; public Config getConfig() { if (config == null) { // 第一次检查 synchronized (this) { if (config == null) { // 第二次检查 config = loadFromDB(); } } } return config; } }
-
价值:避免重复初始化,保证线程安全的同时减少锁开销。
3.4、异步刷新机制
scss
java
scheduleRefresh(e, key, hash, value, now, loader); // 后台刷新
借鉴点:
-
业务场景:热点数据自动刷新(如商品价格、实时榜单)
-
实现方式:
typescriptjava // 使用 CompletableFuture 异步更新 public class ProductCache { private ConcurrentMap<String, Product> cache = new ConcurrentHashMap<>(); public Product getProduct(String id) { return cache.compute(id, (k, v) -> { if (v == null || v.isExpired()) { return loadProduct(id); // 同步加载 } else { CompletableFuture.runAsync(() -> { Product newVersion = reloadProduct(id); cache.put(id, newVersion); }); return v; // 返回旧值,异步更新 } }); } }
-
价值:用户无感知刷新,避免阻塞请求。
3.5、失效策略与惰性清理
scss
java
getLiveValue(e, now); // 检查过期
postReadCleanup(); // 读后清理(惰性)
借鉴点:
-
业务场景:缓存过期策略(如用户会话30分钟失效)
-
实现方式:
csharpjava // 自定义带过期时间的缓存 public class ExpiringCache<K, V> { private class Entry { V value; long expireTime; } private ConcurrentHashMap<K, Entry> map = new ConcurrentHashMap<>(); public V get(K key) { Entry entry = map.get(key); if (entry != null && System.currentTimeMillis() < entry.expireTime) { return entry.value; } else { map.remove(key); return null; } } }
-
价值:无需定时任务扫描,按需清理节省资源。
3.6、等待正在进行的加载
scss
java
waitForLoadingValue(e, key, valueReference); // 等待其他线程加载
借鉴点:
-
业务场景:防止重复加载(如数据库查询防穿透)
-
实现方式:
inijava public class LoadingCache<K, V> { private ConcurrentMap<K, Future<V>> futures = new ConcurrentHashMap<>(); public V get(K key) throws Exception { Future<V> future = futures.get(key); if (future == null) { FutureTask<V> task = new FutureTask<>(() -> loadFromDB(key)); future = futures.putIfAbsent(key, task); if (future == null) { task.run(); } } return future.get(); } }
-
价值:避免多个线程同时加载同一资源。
3.7、统计与监控
scss
java
statsCounter.recordHits(1); // 记录命中
借鉴点:
-
业务场景:服务监控(如接口调用次数、缓存命中率)
-
实现方式:
javajava // 使用 Micrometer 指标库 public class OrderService { private final Counter cacheHits = Metrics.counter("order.cache.hits"); private final Counter cacheMiss = Metrics.counter("order.cache.miss"); public Order getOrder(String id) { Order order = cache.get(id); if (order != null) { cacheHits.increment(); } else { cacheMiss.increment(); } return order; } }
-
价值:通过监控数据优化系统性能。
3.8、异常处理规范化
arduino
java
catch (ExecutionException ee) {
// 统一异常转换
}
借鉴点:
-
业务场景:统一异常处理(如将底层异常转换为业务异常)
-
实现方式:
csharpjava try { // 调用第三方接口 } catch (IOException e) { throw new BusinessException("网络异常,请重试", e); } catch (SQLException e) { throw new BusinessException("数据库错误", e); }
-
价值:避免底层异常扩散到上层,提高可维护性。
3.9、引用类型管理
ini
java
ValueReference<K, V> valueReference = e.getValueReference();
借鉴点:
-
业务场景:内存敏感缓存(如图片缩略图缓存)
-
实现方式:
csharpjava // 使用软引用缓存大对象 public class ImageCache { private Map<String, SoftReference<BufferedImage>> cache = new HashMap<>(); public BufferedImage getImage(String path) { SoftReference<BufferedImage> ref = cache.get(path); BufferedImage img = ref != null ? ref.get() : null; if (img == null) { img = loadImage(path); cache.put(path, new SoftReference<>(img)); } return img; } }
-
价值:在内存不足时自动释放,防止 OOM。
3.10、时间源抽象
arduino
java
long now = map.ticker.read(); // 可替换的时间源
借鉴点:
-
业务场景:单元测试时间模拟(如优惠券过期测试)
-
实现方式:
csharpjava public interface Clock { long currentTimeMillis(); } public class SystemClock implements Clock { public long currentTimeMillis() { return System.currentTimeMillis(); } } // 测试时替换为固定时间 public class FixedClock implements Clock { private final long time; public FixedClock(long time) { this.time = time; } public long currentTimeMillis() { return time; } }
-
价值:提高代码可测试性。
3.11、模板方法模式
整体 get
方法的结构体现了模板模式:
- 参数校验 → 2. 尝试获取 → 3. 加载/等待 → 4. 异常处理 → 5. 清理
借鉴点:
-
业务场景:统一流程控制(如订单创建流程)
-
实现方式:
scssjava public abstract class OrderCreator { public final Order createOrder(OrderRequest request) { validateRequest(request); // 1. 校验 checkInventory(request); // 2. 库存检查 Order order = buildOrder(request); // 3. 构造订单 saveToDB(order); // 4. 持久化 sendEvent(order); // 5. 发事件 return order; } protected abstract void sendEvent(Order order); }
-
价值:规范流程,子类只需实现特定步骤。
3.12、线程安全与可见性
通过 volatile
、synchronized
等保证状态可见性。
借鉴点:
-
业务场景:多线程共享标志位(如服务开关)
-
实现方式:
typescriptjava public class FeatureToggle { private volatile boolean newFeatureEnabled = false; public void enableFeature() { newFeatureEnabled = true; // 对其他线程立即可见 } public boolean isEnabled() { return newFeatureEnabled; } }
-
价值:避免因可见性问题导致逻辑错误。
4、总结
这些设计原则可应用于以下业务场景:
- 高并发接口:分段锁 + 异步刷新
- 缓存系统:失效策略 + 双重检查锁定
- 资源管理:软/弱引用 + 惰性清理
- 监控系统:统计计数器 + 统一异常
- 流程控制:模板方法 + 防御性校验
核心思想:在保证正确性的前提下,通过减少锁粒度、异步化、惰性处理等手段提升性能。开发中应根据具体场景灵活组合这些模式。