1

我正在尋找一種方法來次相同的密鑰插入嘗試次數的軌道保持到Map在多線程environemnt使得Map可同時由多個線程讀取和更新。如果保持跟蹤重複密鑰插入嘗試不容易實現,則替代解決方案是在重複密鑰插入嘗試的第一個符號處終止應用程序。保持重複插入的軌道在一個Map(多線程環境)

以下用戶定義的單例Spring bean顯示了我的應用程序使用的全局緩存,該緩存使用多個分區的彈簧批處理作業(每個要加載的DataType需要一個作業)加載。 addResultForDataType方法可以被多個線程同時調用。

public class JobResults { 

    private Map<DataType, Map<String, Object>> results; 

    public JobResults() { 
     results = new ConcurrentHashMap<DataType, Map<String, Object>>(); 
    } 

    public void addResultForDataType(DataType dataType, String uniqueId, Object result) { 
     Map<String, Object> dataTypeMap = results.get(dataType); 
     if (dataTypeMap == null) { 
      synchronized (dataType) { 
       dataTypeMap = results.get(dataType); 
       if (dataTypeMap == null) { 
        dataTypeMap = new ConcurrentHashMap<String, Object>(); 
        results.put(dataType, dataTypeMap); 
       } 
      } 
     } 
     dataTypeMap.put(uniqueId, result); 
    } 

    public Map<String, Object> getResultForDataType(DataType dataType) { 
     return results.get(dataType); 
    } 

} 

這裏:

  • DataType可以爲其中的數據被加載從 表名或文件名被認爲。每個DataType指示一個表或文件。
  • uniqueId表示表或文件中每個記錄的主鍵。
  • result是表示整行的對象。
  • 上述方法每個記錄調用一次。在任何給定時間,多個線程可以插入相同的DataType或不同的DataType的記錄。

我想創造另一個地圖,以保持重複插入的軌跡:

public class JobResults { 

    private Map<DataType, Map<String, Object>> results; 
    private Map<DataType, ConcurrentHashMap<String, Integer>> duplicates; 

    public JobResults() { 
     results = new ConcurrentHashMap<DataType, Map<String, Object>>(); 
     duplicates = new ConcurrentHashMap<DataType, ConcurrentHashMap<String, Integer>>(); 
    } 

    public void addResultForDataType(DataType dataType, String uniqueId, Object result) { 
     Map<String, Object> dataTypeMap = results.get(dataType); 
     ConcurrentHashMap<String,Integer> duplicateCount = duplicates.get(dataType); 
     if (dataTypeMap == null) { 
      synchronized (dataType) { 
       dataTypeMap = results.get(dataType); 
       if (dataTypeMap == null) { 
        dataTypeMap = new ConcurrentHashMap<String, Object>(); 
        duplicateCount = new ConcurrentHashMap<String, Integer>(); 
        results.put(dataType, dataTypeMap); 
        duplicates.put(dataType, duplicateCount); 
       } 
      } 
     } 
     duplicateCount.putIfAbsent(uniqueId, 0); 
     duplicateCount.put(uniqueId, duplicateCount.get(uniqueId)+1);//keep track of duplicate rows 
     dataTypeMap.put(uniqueId, result); 
    } 

    public Map<String, Object> getResultForDataType(DataType dataType) { 
     return results.get(dataType); 
    } 

} 

我意識到statemet duplicateCount.put(uniqueId, duplicateCount.get(uniqueId)+1);沒有隱含線程安全的。爲了使其線程安全,我將需要使用同步,這會減慢我的插入。如何在不影響應用程序性能的情況下跟蹤重複插入。如果保留重複插入的軌跡並不容易,那麼只要在嘗試覆蓋地圖中現有條目的第一個符號處引發異常就可以了。我知道Map不允許重複的鍵。我想要的是一種跟蹤任何此類嘗試的方法,並暫停應用程序,而不是覆蓋Map中的條目。

+0

您是否在尋找避免對外部或內部'Map'的重複? – Actorclavilis

+0

@Actorclavilis只是外面的地圖。 (請參閱我的解決方案嘗試)。 – Ping

回答

1

嘗試這樣:

ConcurrentHashMap<String, AtomicInteger> duplicateCount = new ConcurrentHashMap<String, AtomicInteger>(); 

然後,當你準備遞增計數,這樣做:

final AtomicInteger oldCount = duplicateCount.putIfAbsent(uniqueId, new AtomicInteger(1)); 
if (oldCount != null) { 
    oldCount.incrementAndGet(); 
} 

所以,如果你沒有在地圖上的計數但,你會把1,如果你有,你會得到當前值並自動增加它。這應該是線程安全的。

+1

使用原子一個接一個不能使整個原子爲原子 – UmNyobe

+0

它或者將一個新的AtomicInteger與值1相加,或者增加一個現有的AtomicInteger。它不會同時使用兩者。我猜,你總是可以這樣做: final AtomicInteger oldCount = duplicateCount.putIfAbsent(uniqueId,new AtomicInteger(1)); (oldCount!= null){ oldCount.incrementAndGet();如果(oldCount!= null){ } } –

+0

您的代碼中的警告是,在讀取oldCount和執行空檢查之間沒有建立「發生之前」關係。顯然,在目前的情況下 - 這並不重要,但值得一提的是它。 – kgdinesh

0

如果你想跟蹤刀片的數量,可以將外部映射類型更改爲類似Map<String, Pair<Integer, Object>>(或者,如果你不使用Apache的風景,只是Map<DataType, Map.Entry<Integer, InnerType>>,其中Integer值是多少更新:

DataType key = ...; 
Map<Integer, Object> value = ...; 
dataTypeMap.compute(key, (k, current) -> { 
    if (current == null) { 
     /* Initial count is 0 */ 
     return Pair.of(0, value); 
    } else { 
     /* Increment count */ 
     return Pair.of(current.getFirst(), value); 
    })); 

如果你所關心的是確保有沒有重複的插入,你可以簡單地使用computeIfAbsent

DataType key = ...; 
Map<Integer, Object> value = ...; 
if (dataTypeMap.computeIfAbsent(key, k -> value)) != null) { 
    /* There was already a value */ 
    throw new IllegalStateException(...); 
}); 
+0

感謝您的回答;但是請注意,更改「Map」的結構不是一種選擇。 – Ping