【问题标题】:Doubly Check Lock Dosen't work on this java code?Double Check Lock 不适用于此 java 代码?
【发布时间】:2020-05-15 21:45:41
【问题描述】:

我正在尝试通过优化同步块来修复服务。我得到了两个不同的值,我的双重检查单例与一个易失的字符串似乎不起作用?

get 和 Increment 字符串在 DB 上设置行锁并递增字符串,因此仅在 DB 级别处理唯一更新。所以在第一种情况下,没有问题。

问题在于else blozk。当相关 ID 不为空时,如果这是第一次调用,我们会尝试获取已映射的值。然后我们首先映射该值,然后返回它。此映射必须同步,以便两个不同的线程不会更新下一个 val agter,它们都发现它为空。

这个类也是一个单例服务。

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

【问题讨论】:

  • 你能说明你是如何使用这个方法的吗?还有,为什么不能同步整个方法?
  • 如果相关 id 为空,同步整个方法会导致同步,但在这种情况下不需要同步。
  • 您是否尝试将额外的nextValue = mapper.mapToB(SOME_CONST, correlationId); 作为同步块的第一行?
  • 之前的代码是如何被破解的,需要优化锁定?您如何确定这是问题所在?

标签: java multithreading thread-safety singleton locking


【解决方案1】:

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

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

问题是 19 由 A 线程返回,等待的线程在 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 的其他调用。

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

【讨论】:

    猜你喜欢
    • 2021-02-17
    • 2021-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多