我有一个像这样的缓存实现:
class X
{
private final Map<String, ConcurrentMap<String, String>> structure = new HashMap...();
public String getValue(String context, String id)
{
// just assume for this example that there will be always an innner map
final ConcurrentMap<String, String> innerStructure = structure.get(context);
String value = innerStructure.get(id);
if(value == null)
{
synchronized(structure)
{
// can I be sure, that this inner map will represent the last updated
// state from any thread?
value = innerStructure.get(id);
if(value == null)
{
value = getValueFromSomeSlowSource(id);
innerStructure.put(id, value);
}
}
}
return value;
}
}
这个实现是否是线程安全的?我可以确定从synchronized块中的任何线程获取最后更新的状态吗?如果我使用java.util.concurrent.ReentrantLock而不是synchronized块,这种行为是否会改变,如下所示:
...
if(lock.tryLock(3, SECONDS))
{
try
{
value = innerStructure.get(id);
if(value == null)
{
value = getValueFromSomeSlowSource(id);
innerStructure.put(id, value);
}
}
finally
{
lock.unlock();
}
}
...
我知道最终实例成员在线程之间是同步的,但这对于这些成员持有的对象也是如此吗?
也许这是一个愚蠢的问题,但我不知道如何测试它以确保它适用于每个操作系统和每个架构。
答案 0 :(得分:1)
首先,这不是一个愚蠢的问题。同步真的很难做到,而且我并不自称是专家。
在您的程序中,在指定的上下文中,是的,您可以假设您获得的String
是最新版本。但是,您的代码仍然不安全,因为您正在从Map
块之外的synchronized
读取值。如果此读取发生在Map
插入值的同时,则无法保证返回合理的值。我知道至少在某些实现中,由于实现中的一些奇怪现象,这可能导致无限循环。
简短版本是你不应该有一个由多个线程读取或写入的结构,除非你使用synchronized
或锁等同步原语来保护它,或者除非该结构专门设计为像ConcurrentHashMap
一样无锁。
在这种情况下,您确实可以使用ReentrantLock
来保护对结构的访问并进行定时等待,但是如果您这样做,则必须保证结构的任何读取也受到保护。同样的锁。否则,您会冒多线程看到数据不一致或损坏的风险。
答案 1 :(得分:1)
在X类实现的情况下,只要值不为null,就可以更新innerStructure的值。因此,假设innerStructure的所有值都不为空。假设在没有getValue()的情况下没有更新结构和innerStructure,这是线程安全的。
如果结构和innerStructure在其他函数中更新,则getValue返回的值有可能不会将其变为最新值。
虽然看起来性能的改善是一个目的, 问题和解决方法在以下网站上描述。
http://www.ibm.com/developerworks/java/library/j-jtp03304/
(这是否解决了双重检查锁定问题)
答案 2 :(得分:0)
您可能需要将ConcurrentHashMap用于外部Hashmap和InnerHashMap,以便在Bucket级别同步获取和放置操作