我遇到了使用putIfAbsent的问题,其中第二个线程将在第一个线程完成使用pk更新值之前尝试访问该值。
示例代码。
public <T> Object getLookupValue(final Class<T> type, String key, ConcurrentHashMap<String, T> concurrentMap) {
try {
T value = concurrentMap.get(key);
if (value == null) {
System.out.println("save");
T t = type.getDeclaredConstructor(String.class).newInstance(key);
Object returnedValue = concurrentMap.putIfAbsent(key, t);
if (returnedValue == null) {
System.out.println("session save");
session.save(t);
System.out.println("t ouput " + t.toString());
return t;
}
return concurrentMap.get(key);
} else {
System.out.println("update" + concurrentMap.get(name));
return concurrentMap.get(key);
}
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException ex) {
System.out.println("getLookupValue " + ex);
Logger.getLogger(LineReaderParserImpl.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
输出
key 2008 thread 1
save
session save
key 2008 thread 0
update Year{name =2008, pk =null}
year pk null thread 0
save
session save
t ouput Year{name =2008, pk =1}
有人知道为什么在线程0完成添加pk之前调用线程1,或者为什么线程0在生成pk之前添加了对象?
答案 0 :(得分:2)
来自ConcurrentHashMap
API:
“但是,即使所有操作都是线程安全的,检索操作也不需要锁定,并且没有任何支持以阻止所有访问的方式锁定整个表。”
在方法参数中声明您的ConcurrentHashMap
final,并在同步块中对其进行编辑。
public foo(final ConcurrentHashMap concurrentMap) {
synchronized (concurrentMap) {
//Your code here
}
}
这会强制每个线程在mergeMap Object
上检索锁定,然后再修改它,这将解决你的竞争条件。
此外,如果您需要多个线程同时访问地图,但在上述foo()
方法中执行应用程序代码时只需要锁定,请为要获取的方法创建锁定,而不是使用地图本身。
final Object fooLock = new Object();
public foo(final ConcurrentHashMap concurrentMap) {
synchronized (fooLock) {
//Your code here
}
}
关于第二个例子的更多解释:
假设我有一个ConcurrentHashMap,其中包含String的键,其值为Year。不同的线程可以访问它来添加/删除值,我想在一定范围内对Year进行分析,同时在我的分析运行时不阻止我的程序添加/删除值。
如果锁定ConcurrentHashMap,其他线程将无法添加/删除值,直到锁定被删除。
在第二个例子中,我为要抓取的方法创建了一个不同的锁,因此它不会锁定地图本身。
ConcurrentHashMap<String, Year> concurrentMap;
final Object lock = new Object();
public void runAnalysis(final ConcurrentHashMap map) {
/*synchronized (map) {
//This will cause addValue() to lock up while the analysis is running
}*/
synchronized (lock) {
//Now we can run a long-running analysis and not block the addValue() method
//Additionally, if another thread calls runAnalysis(), it must wait to
//get our lock (when a current running analysis is completed)
//before it can start
}
}
//This method needs access to concurrentMap, so we can't lock it
public void addValue() {
concurrentMap.add("key", new Year());
}
假设这是在Analyzer
类中声明的。我也可以像这样声明runAnalysis()方法:
public synchronized void runAnalysis(ConcurrentHashMap map) {
//Do analysis logic
}
此方法不会抓取我们的“锁定”对象上的锁定,而是获取Analyzer
实例上的锁定。这种做法略有不同,通常比创建自己的锁更常见。
请注意,如果我执行此操作,则在runAnalysis()运行时将阻止任何其他声明为“synchronized”的方法,反之亦然。您可以使用锁获得尽可能复杂的内容,但是如果您只需要一个方法来同步类实例,那么抓取实例锁而不是单独为该方法创建锁定会更加清晰
您应该在Java中查找有关多线程,同步和锁的一些教程。