可能遇到的问题
- 线程安全问题:
- 享元对象如果是共享的可变对象,多个线程同时访问和修改时可能导致数据不一致。例如,一个共享的文本格式享元对象,多个线程同时尝试修改其字体颜色属性,可能导致最终颜色状态混乱。
- 资源竞争问题:
- 在高并发场景下,对享元池资源的获取和释放可能成为性能瓶颈。比如,多个线程同时请求获取相同类型的享元对象,可能造成等待,降低系统整体性能。
- 内存压力问题:
- 虽然享元模式旨在减少内存消耗,但在高并发且长时间运行的应用中,如果享元对象不能及时释放,可能导致内存持续增长,最终引发内存溢出。例如,大量临时使用的享元对象一直保留在享元池中,未被回收。
优化方法
- 线程安全机制:
- 不可变享元对象:将享元对象设计为不可变对象,这样多个线程访问时无需额外同步机制。例如,对于表示颜色的享元对象,一旦创建其颜色值就不可改变。代码示例:
public final class ColorFlyweight {
private final String color;
public ColorFlyweight(String color) {
this.color = color;
}
public String getColor() {
return color;
}
}
- 同步访问享元池:在享元池的获取和释放操作上使用同步机制。可以使用
synchronized
关键字或者 ConcurrentHashMap
等线程安全的集合类来管理享元池。示例代码使用 ConcurrentHashMap
:
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class FlyweightFactory {
private static final ConcurrentMap<String, Flyweight> flyweightMap = new ConcurrentHashMap<>();
public static Flyweight getFlyweight(String key) {
return flyweightMap.computeIfAbsent(key, k -> new ConcreteFlyweight(k));
}
}
- 资源管理:
- 使用弱引用或软引用:对于享元对象,可以使用
WeakReference
或 SoftReference
来管理,这样在内存不足时,享元对象可能被垃圾回收机制回收,避免内存溢出。例如:
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
public class WeakFlyweightFactory {
private static final Map<String, WeakReference<Flyweight>> flyweightMap = new HashMap<>();
public static Flyweight getFlyweight(String key) {
WeakReference<Flyweight> weakReference = flyweightMap.get(key);
Flyweight flyweight = weakReference!= null? weakReference.get() : null;
if (flyweight == null) {
flyweight = new ConcreteFlyweight(key);
flyweightMap.put(key, new WeakReference<>(flyweight));
}
return flyweight;
}
}
- 设置享元对象的生命周期:为享元对象设置合理的生命周期,例如使用定时器定期清理长时间未使用的享元对象。可以使用
ScheduledExecutorService
来实现:
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class FlyweightManager {
private static final Map<String, Long> lastUsedMap = new HashMap<>();
private static final Map<String, Flyweight> flyweightMap = new HashMap<>();
private static final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
static {
executor.scheduleAtFixedRate(() -> {
long currentTime = System.currentTimeMillis();
lastUsedMap.entrySet().removeIf(entry -> currentTime - entry.getValue() > 60000); // 60秒未使用则移除
lastUsedMap.keySet().forEach(key -> flyweightMap.remove(key));
}, 0, 10, TimeUnit.SECONDS);
}
public static Flyweight getFlyweight(String key) {
Flyweight flyweight = flyweightMap.get(key);
if (flyweight!= null) {
lastUsedMap.put(key, System.currentTimeMillis());
} else {
flyweight = new ConcreteFlyweight(key);
flyweightMap.put(key, flyweight);
lastUsedMap.put(key, System.currentTimeMillis());
}
return flyweight;
}
}