簡體   English   中英

以下代碼線程安全

[英]Is following code Thread safe

我有一個場景,我必須維護一個可以由多個線程填充的Map,每個線程都修改相應的List(唯一標識符/鍵是線程名稱),當線程的列表大小超過固定批量大小時,我們必須保持DB中的記錄。

示例代碼如下:

private volatile ConcurrentHashMap<String, List<T>>  instrumentMap = new ConcurrentHashMap<String, List<T>>();
private ReadWriteLock lock ;

public void addAll(List<T> entityList, String threadName) {
    try {
        lock.readLock().lock();
        List<T> instrumentList = instrumentMap.get(threadName);
        if(instrumentList == null) {
            instrumentList = new ArrayList<T>(batchSize);
            instrumentMap.put(threadName, instrumentList);
        }

        if(instrumentList.size() >= batchSize -1){
            instrumentList.addAll(entityList);
            recordSaver.persist(instrumentList); 
            instrumentList.clear();
        } else {
            instrumentList.addAll(entityList);  
        }
    } finally {
        lock.readLock().unlock();
    }

}

每隔2分鍾就會再運行一個單獨的線程來保存Map中的所有記錄(以確保每隔2分鍾后我們會持續存在一些內容並且地圖大小不會太大)並且當它啟動時它會阻止所有其他線程(檢查readLock和writeLock usawhere writeLock具有更高的優先級)

if(//Some condition) {
                    Thread.sleep(//2 minutes);
                    aggregator.getLock().writeLock().lock();
                    List<T> instrumentList = instrumentMap .values().stream().flatMap(x->x.stream()).collect(Collectors.toList());
                    if(instrumentList.size() > 0) {

                        saver.persist(instrumentList);
                        instrumentMap .values().parallelStream().forEach(x -> x.clear());
                    aggregator.getLock().writeLock().unlock();
                }

這個解決方案幾乎適用於我們測試的每個場景,除非有時候我們看到一些記錄丟失了,盡管它們在Map中添加得很好但根本沒有保留

我的問題是這段代碼有什么問題? ConcurrentHashMap不是最好的解決方案嗎? 讀/寫鎖的使用在這里有問題嗎? 我應該順序處理嗎?

不,這不是線程安全的。

問題是您正在使用ReadWriteLock的鎖定。 這並不保證進行更新的獨占訪問權限。 您需要使用鎖定。

但是你根本不需要使用單獨的鎖。 您只需使用ConcurrentHashMap.compute方法:

instrumentMap.compute(threadName, (tn, instrumentList) -> {
  if (instrumentList == null) {
    instrumentList = new ArrayList<>();
  }

  if(instrumentList.size() >= batchSize -1) {
    instrumentList.addAll(entityList); 
    recordSaver.persist(instrumentList); 
    instrumentList.clear();
  } else {
    instrumentList.addAll(entityList);
  }

  return instrumentList;
});

這允許您更新列表中的項目,同時還保證對給定鍵的列表的獨占訪問權限。

我懷疑您可以將compute調用拆分為computeIfAbsent (如果不存在則添加列表),然后是computeIfPresent (更新/持久化列表):這里不需要這兩個操作的原子性。 但是將它們分開並沒有真正的意義。


另外, instrumentMap幾乎肯定不應該是易變的。 除非你真的想重新分配它的價值(給定這個代碼,我懷疑),刪除volatile並使其成為最終。

同樣,非最終鎖也是有問題的。 如果你堅持使用鎖定,也要做到最后。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM