我正在尝试通过优化同步块(synchronized block)来修复服务。我得到两个不同的值,并且我的带有 volatile 字符串的双重检查单例似乎不起作用?
get 和 Increment 字符串在数据库上放置行锁并递增字符串,以便仅在数据库级别进行唯一更新。所以在第一种情况下,没有问题。
问题出在 else block 上。当相关 ID 不为空时,如果这是第一次调用,我们将尝试获取已映射的值。然后我们首先映射该值,然后返回它。此映射必须同步,以便两个不同的线程在发现下一个 val 为 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
最佳答案
如果我以正确的方式理解您,您会期望一个线程(我们称之为 A)等待另一个线程(将值增加到 19,B),然后跳过增量,因为 nextValue
是 19 并且不为 null。但等待线程看不到更改。
据我所知,可能的情况要复杂得多:
问题在于 A 线程返回 19,即等待的线程,因为在 B 线程在以下行发布 volatile 值后,它立即跳过整个 block :
nextValue = rangeFetch.getAndIncrementAsString(name);
另一种情况,A线程进入该方法并且nextValue
已经发布。
所以它立即跳转到return语句并返回19,这是由B线程设置的(是的,这是意外的,但有时会发生这种情况)。您不应期望 A 线程等待 B 完成执行。 B(首先到达同步块(synchronized block))完成处理(递增)值并返回 20。
不过,有可能的解决方法:
if (nextValue == null) {
synchronized(this) {
if(nextValue == null) {
String localTemp = rangeFetch.getAndIncrementAsString(name);
idMapper.mapToB(SOME_CONST, correlationId, localTemp, DURATION);
nextValue = localTemp;
}
}
}
总体来说,对 nextValue
所做的更改立即会影响 getNextIncrement
的其他调用。
在这种模式下调试问题真的很困难,所以我可能是错的,但无论如何我都会发布一个答案,因为我的解释对于评论来说太长了。
关于java - Double Check Lock 在此 java 代码上不起作用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59983281/