WeakConcurrentMap
# 功能介绍
WeakConcurrentMap是Hutool中提供的基于弱引用的并发Map实现,它结合了ConcurrentHashMap的并发安全特性和弱引用的自动回收机制。与SoftConcurrentMap不同,弱引用对象在JVM进行垃圾回收时会被立即回收,无论内存是否充足,这使得WeakConcurrentMap非常适合缓存那些生命周期较短、容易被创建且不希望影响内存回收的对象。
# 主要特性
- 基于弱引用实现,垃圾回收时自动回收对象
- 并发安全,支持多线程操作
- 基于ConcurrentHashMap实现,性能优异
- 支持所有Map接口方法
- 支持null值,但不支持null键
- 支持自动清理过期条目
- 支持获取值时的自动加载(通过实现Loader接口)
- 线程安全,适合多线程环境使用
- 支持自定义引用队列和清理策略
- 弱引用对象在垃圾回收时立即回收,不考虑内存情况
- 适合缓存短生命周期对象
# 适用场景
- 实现防止内存泄漏的缓存系统
- 缓存短生命周期对象
- 缓存容易被创建的对象
- 实现对象关联映射
- 多线程环境下的临时对象缓存
- 事件监听器映射
- 任何需要自动快速回收对象的Map场景
- 避免内存泄漏的场景
# 使用示例
# 示例1:基本用法
// 创建WeakConcurrentMap实例
WeakConcurrentMap<String, Object> weakMap = new WeakConcurrentMap<>();
// 创建普通对象
Object obj1 = new Object();
Object obj2 = new Object();
// 添加对象到缓存
weakMap.put("key1", obj1);
weakMap.put("key2", obj2);
// 获取对象
Object retrieved1 = weakMap.get("key1");
// 输出: java.lang.Object@...
// 解除强引用,使对象成为弱引用状态
obj1 = null;
obj2 = null;
// 手动触发垃圾回收
System.gc();
// 再次获取对象,弱引用对象已被回收
Object retrieved2 = weakMap.get("key1");
// 输出: null
Object retrieved3 = weakMap.get("key2");
// 输出: null
# 示例2:使用Loader自动加载
// 创建WeakConcurrentMap实例,使用Loader自动加载值
WeakConcurrentMap<String, String> weakMap = new WeakConcurrentMap<>((key) -> {
// 模拟从数据库或其他数据源加载数据
Console.log("Loading data for key: " + key);
return "Loaded value for " + key;
});
// 获取值,如果不存在则自动加载
String value1 = weakMap.get("key1");
// 输出: Loading data for key: key1
// 输出: Loaded value for key1
// 解除强引用
value1 = null;
// 手动触发垃圾回收
System.gc();
// 再次获取,对象已被回收,会重新加载
String value2 = weakMap.get("key1");
// 输出: Loading data for key: key1
// 输出: Loaded value for key1
# 示例3:遍历和清理
// 创建WeakConcurrentMap实例
WeakConcurrentMap<String, String> weakMap = new WeakConcurrentMap<>();
weakMap.put("key1", "value1");
weakMap.put("key2", "value2");
weakMap.put("key3", "value3");
// 遍历Map
weakMap.forEach((key, value) -> {
Console.log(key + "=" + value);
});
// 输出:
// key1=value1
// key2=value2
// key3=value3
// 解除强引用并触发垃圾回收
System.gc();
// 手动清理已回收的条目
int cleanedCount = weakMap.cleanUp();
// 输出: 3(假设所有对象都被回收)
// 获取Map大小
int size = weakMap.size();
// 输出: 0
// 清空Map
weakMap.clear();
# 示例4:事件监听器映射
// 创建WeakConcurrentMap实例,用于存储事件监听器
WeakConcurrentMap<String, EventListener> listenerMap = new WeakConcurrentMap<>();
// 添加事件监听器
EventListener listener1 = new EventListener() {
@Override
public void onEvent(Event event) {
Console.log("Listener1 received event: " + event);
}
};
EventListener listener2 = new EventListener() {
@Override
public void onEvent(Event event) {
Console.log("Listener2 received event: " + event);
}
};
listenerMap.put("listener1", listener1);
listenerMap.put("listener2", listener2);
// 触发事件
Event event = new Event("test");
for (EventListener listener : listenerMap.values()) {
if (listener != null) {
listener.onEvent(event);
}
}
// 解除强引用
listener1 = null;
// 手动触发垃圾回收
System.gc();
// 再次触发事件,listener1已被回收,只会调用listener2
for (EventListener listener : listenerMap.values()) {
if (listener != null) {
listener.onEvent(event);
}
}
# 注意事项
- WeakConcurrentMap是线程安全的,适合多线程环境使用
- 基于弱引用实现,垃圾回收时立即回收对象,不考虑内存情况
- 不支持null键,但支持null值
- 性能与ConcurrentHashMap相当,适合大多数场景
- 使用Loader接口可以实现自动加载功能,类似于Guava Cache
- 建议定期调用cleanUp()方法清理已回收的条目,避免内存泄漏
- 弱引用对象的回收时机取决于JVM的垃圾回收策略,无法精确控制
- 适合缓存短生命周期、容易创建的对象,不适合缓存长期存在的对象
- 可以通过构造函数自定义引用队列和Loader
- 弱引用对象在没有强引用指向时,会在下次垃圾回收时被回收
# 方法列表
WeakConcurrentMap实现了ConcurrentMap接口,因此支持所有Map接口方法,主要包括:
| 方法名 | 描述 | 参数 | 返回值 |
|---|---|---|---|
| WeakConcurrentMap() | 创建默认的WeakConcurrentMap | 无 | 无 |
| WeakConcurrentMap(ReferenceQueue | 使用自定义引用队列创建WeakConcurrentMap | queue: 引用队列 | 无 |
| WeakConcurrentMap(Loader<K, V> loader) | 使用Loader创建WeakConcurrentMap,支持自动加载 | loader: 加载器 | 无 |
| WeakConcurrentMap(Loader<K, V> loader, ReferenceQueue | 使用Loader和引用队列创建WeakConcurrentMap | loader: 加载器, queue: 引用队列 | 无 |
| put(K key, V value) | 添加键值对,值使用弱引用包装 | key: 键, value: 值 | V |
| get(Object key) | 获取值,如果值已被回收且设置了Loader,则自动加载 | key: 键 | V |
| remove(Object key) | 移除键值对 | key: 键 | V |
| clear() | 清空所有键值对 | 无 | void |
| size() | 获取Map大小(包括已回收但未清理的条目) | 无 | int |
| cleanUp() | 清理已回收的条目 | 无 | int |
| containsKey(Object key) | 判断是否包含指定键 | key: 键 | boolean |
| containsValue(Object value) | 判断是否包含指定值 | value: 值 | boolean |
| keySet() | 获取所有键的集合 | 无 | Set |
| values() | 获取所有值的集合(包括已回收的值,返回null) | 无 | Collection |
| entrySet() | 获取所有键值对的集合 | 无 | Set<Map.Entry<K, V>> |
| forEach(BiConsumer<? super K, ? super V> action) | 遍历所有键值对 | action: 遍历操作 | void |
| replace(K key, V oldValue, V newValue) | 替换键值对(并发安全) | key: 键, oldValue: 旧值, newValue: 新值 | boolean |
| putIfAbsent(K key, V value) | 如果键不存在则添加键值对(并发安全) | key: 键, value: 值 | V |