有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

多线程双重检查锁在这个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) 个答案

  1. # 1 楼答案

    如果我以正确的方式理解了您,那么您希望一个线程(我们称之为A)等待另一个线程(它将值增加到19,B),然后跳过增量,因为nextValue是19且不为空。但是等待的线程看不到变化

    如我所见,可能的情况要复杂得多:

    问题是A线程返回19,该线程等待,因为在B线程在第行发布volatile值后,它立即跳过整个块:

    nextValue = rangeFetch.getAndIncrementAsString(name);

    另一种情况是,A线程进入该方法,nextValue已经发布

    因此它立即跳转到return语句并返回19,这是由B线程设置的(是的,这是意外的,但有时会发生)。您不应该期望A线程等待B完成执行。B(首先到达同步块)完成处理(递增)该值并返回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的另一个调用

    在这种模式下调试问题确实很困难,所以我可能是错的,但我还是发布了一个答案,因为我的解释太长,无法给出评论