Double Check Lock 不能在這個 java 代碼上工作? (Doubly Check Lock Dosen't work on this java code?)


問題描述

Double Check Lock 不能在這個 java 代碼上工作? (Doubly Check Lock Dosen't work on this java code?)

我正在嘗試通過優化同步塊來修復服務。我得到兩個不同的值,我的雙重檢查單例與易失性字符串似乎不起作用?

get 和 Increment 字符串在 DB 上設置行鎖並遞增字符串,以便進行唯一更新只關心數據庫級別。所以在第一種情況下,沒有問題。

問題在於else blozk。當相關 ID 不為空時,如果這是第一次調用,我們會嘗試獲取已映射的值。然後我們首先映射該值,然後返回它。這個映射必須同步,這樣兩個不同的線程就不會更新下一個 val agter,它們都發現它是 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。


參考解法

方法 1:

If I understood you in the right way, you expect one thread (lets call it A) to wait for another (which increments the value to 19, B), and then skip the incrementing because nextValue is 19 and not null. But the change is unseen by the waiting thread.

The possible scenario is much more complicated, as I see:

The problem is that 19 returned by the A thread, the one which waits, as it immediately skips the whole block after the volatile value is posted by the B thread at line:

nextValue = rangeFetch.getAndIncrementAsString(name);

Another case, the A thread enters the method and nextValue is already posted.

So it immediately jumps to return statement and return 19, which was set by the B thread (Yes, it is unexpected, but yet this happens sometimes). You should not expect the A thread to wait for the B to finish executing. The B (which reached synchronized block first) finish processing (incrementing) the value and returns 20.

There are possible workarounds for that, though:

if (nextValue == null) {
    synchronized(this) {
        if(nextValue == null) {
            String localTemp = rangeFetch.getAndIncrementAsString(name);
            idMapper.mapToB(SOME_CONST, correlationId, localTemp, DURATION);
            nextValue = localTemp;
        }
    }
}

The overall point is that changes made to nextValue immediately affect another calls of getNextIncrement.

It is really hard to debug problems in this pattern, so I may be wrong, but I post an answer anyway, since my explanation is too long for the comment.

(by SameerSteyrix)

參考文件

  1. Doubly Check Lock Dosen't work on this java code? (CC BY‑SA 2.5/3.0/4.0)

#locking #java #multithreading #singleton #thread-safety






相關問題

C# / ASP.NET - Web 應用程序鎖定 (C# / ASP.NET - Web Application locking)

在程序文件夾中創建鎖定文件會導致異常 (Creating Lock file in Programs Folder causes exception)

我什麼時候會使用 AutoResetEvent 和 ManualResetEvent 而不是 Monitor.Wait()/Monitor.Pulse()? (When would I use AutoResetEvent and ManualResetEvent instead of Monitor.Wait()/Monitor.Pulse()?)

鎖定一個 JavaScript 函數 (Lock a JavaScript Function)

當只有一個線程寫入共享變量時,我需要鎖嗎? (Do I need a lock when only a single thread writes to a shared variable?)

為什麼 lock(objLock) 比 lock(this) 好 (Why is it better to lock(objLock) than lock(this))

雙重檢查鎖定的修復有什麼問題? (What's wrong with this fix for double checked locking?)

在表格行上調用 Dibs (Calling Dibs on a table row)

我怎樣才能優雅地寫 lock {}? (how can I write lock {} elegantly?)

Double Check Lock 不能在這個 java 代碼上工作? (Doubly Check Lock Dosen't work on this java code?)

LINQPad / LINQ To SQL - 簡單查詢僅在循環內執行時才會引發內存不足 (LINQPad / LINQ To SQL - Simple Query Throws Out of Memory Only When Executed Inside a Loop)

如何在 dynamoDB 中實現 50 次寫入的事務? (How can I implement a transaction of 50 writes in dynamoDB?)







留言討論