我对 Java 中的多线程仍然很不满意。我在这里描述的是我的应用程序的核心,我需要把它做好。该解决方案需要快速运行,并且需要实际安全。这行得通吗?欢迎任何建议/批评/替代解决方案。
我的应用程序中使用的对象生成起来有些昂贵,但很少更改,因此我将它们缓存在 *.temp 文件中。一个线程可能会尝试从缓存中检索给定对象,而另一个线程则尝试在那里对其进行更新。检索和存储的缓存操作被封装在一个 CacheService 实现中。
考虑这种情况:
Thread 1: retrieve cache for objectId "page_1".
Thread 2: update cache for objectId "page_1".
Thread 3: retrieve cache for objectId "page_2".
Thread 4: retrieve cache for objectId "page_3".
Thread 5: retrieve cache for objectId "page_4".
注意:线程 1 似乎检索了一个过时的对象,因为线程 2 有它的更新副本。这完全没问题,所以我不需要任何可以赋予线程 2 优先级的逻辑。
如果我在我的服务上同步检索/存储方法,那么我不必要地减慢线程 3、4 和 5 的速度。多个检索操作在任何给定时间都会有效,但很少会调用更新操作。这就是我想避免方法同步的原因。
我收集我需要在线程 1 和 2 专用的对象上进行同步,这意味着锁定对象注册表。在这里,一个明显的选择是 Hashtable,但同样,Hashtable 上的操作是同步的,所以我正在尝试 HashMap。该映射存储了一个字符串对象,用作同步的锁对象,键/值将是被缓存对象的 id。所以对于对象“page_1”,键是“page_1”,锁定对象是一个值为“page_1”的字符串。
如果我的注册表正确,那么我还想保护它不被太多条目淹没。让我们不要详细说明原因。让我们假设,如果注册表已经超过了定义的限制,它需要用 0 个元素重新初始化。这对于不同步的 HashMap 有点风险,但这种泛滥将是正常应用程序操作之外的事情。这应该是非常罕见的情况,希望永远不会发生。但既然有可能,我想保护自己免受它的伤害。
@Service
public class CacheServiceImpl implements CacheService {
private static ConcurrentHashMap<String, String> objectLockRegistry=new ConcurrentHashMap<>();
public Object getObject(String objectId) {
String objectLock=getObjectLock(objectId);
if(objectLock!=null) {
synchronized(objectLock) {
// read object from objectInputStream
}
}
public boolean storeObject(String objectId, Object object) {
String objectLock=getObjectLock(objectId);
synchronized(objectLock) {
// write object to objectOutputStream
}
}
private String getObjectLock(String objectId) {
int objectLockRegistryMaxSize=100_000;
// reinitialize registry if necessary
if(objectLockRegistry.size()>objectLockRegistryMaxSize) {
// hoping to never reach this point but it is not impossible to get here
synchronized(objectLockRegistry) {
if(objectLockRegistry.size()>objectLockRegistryMaxSize) {
objectLockRegistry.clear();
}
}
}
// add lock to registry if necessary
objectLockRegistry.putIfAbsent(objectId, new String(objectId));
String objectLock=objectLockRegistry.get(objectId);
return objectLock;
}