多线程双重检查锁在这个java代码上不起作用?
我正试图通过优化同步块来修复服务。我得到了两个不同的值,我的带有易失性字符串的双重检查单例似乎不起作用
get和Increment字符串在DB上放置一个行锁,并递增该字符串,以便只在DB级别处理惟一更新。所以在第一种情况下,没有问题
问题在于else blozk。当correlation ID不为null时,如果这是第一次调用,则尝试获取一个已经映射的值。然后我们首先映射值,然后返回它。必须同步此映射,以便两个不同的线程在发现下一个值为null时不会更新下一个值
这个类也是一个单例服务
public class RangeQueryService{
private volatile String nextValue=null;
public String getNextIncrement(String name, String correlationId) throws SomeCheckedException {
try {
if (correlationId == null) {
nextValue = rangeFetch.getAndIncrementAsString(name);
} else { //Enter Sync branch
// mapper Will Return null if no value is mapped.
nextValue = mapper.mapToB(SOME_CONST, correlationId);
// Avoid syncronization overhead if value is already fetched. Only enter if nextVal is null.
if (nextValue == null) {
synchronized (this) {
Doubly Check lock pattern, as two threads can find null simultaneously, and wait on the critical section.
if(nextValue==null){
nextValue = rangeFetch.getAndIncrementAsString(name);
idMapper.mapToB(SOME_CONST, correlationId, nextValue, DURATION);
}
}
}
}
return nextValue;
} catch (Exception e) {
throw new SomeCheckedException("Error!" + e.getMessage());
}
}
它返回19和20。它应该只返回19
输出:
headerAfterProcessOne: 0000000019, headerAfterProcessTwo: 0000000020
# 1 楼答案
如果我以正确的方式理解了您,那么您希望一个线程(我们称之为A)等待另一个线程(它将值增加到19,B),然后跳过增量,因为
nextValue
是19且不为空。但是等待的线程看不到变化如我所见,可能的情况要复杂得多:
问题是A线程返回19,该线程等待,因为在B线程在第行发布volatile值后,它立即跳过整个块:
nextValue = rangeFetch.getAndIncrementAsString(name);
另一种情况是,A线程进入该方法,
nextValue
已经发布因此它立即跳转到return语句并返回19,这是由B线程设置的(是的,这是意外的,但有时会发生)。您不应该期望A线程等待B完成执行。B(首先到达同步块)完成处理(递增)该值并返回20
不过,有可能的解决办法:
总的来说,对
nextValue
所做的更改会立即影响对getNextIncrement
的另一个调用在这种模式下调试问题确实很困难,所以我可能是错的,但我还是发布了一个答案,因为我的解释太长,无法给出评论