簡體   English   中英

沒有同步關鍵字的ConcurrentHashMap

[英]ConcurrentHashMap without synchronized keyword

我有一個ConcurrentHashMap<String,List<String>> ,該線程可以通過在附加值之前檢查鍵是否存在的線程來訪問。 我通過使用synchronized關鍵字來完成此工作。 如果未使用synchronized關鍵字,則值是錯誤的。 ConcurrentHashMap線程安全嗎? 還是此代碼有問題? 是否可以在不使用synchronized情況下使此代碼正常工作以獲得更好的性能? 這是執行此操作的代碼。

ExecutorService executorService = Executors.newFixedThreadPool(4);
        final ConcurrentLinkedQueue<Future<?>> futures = new ConcurrentLinkedQueue<Future<?>>();
        final ConcurrentHashMap<String, List<String>> map = new ConcurrentHashMap<String, List<String>>();
        final JsonParser parser = new JsonParser();
        File[] files = new File(dir).listFiles();
for (final File tfile : files) {
                futures.add((Future<String>) executorService.submit(new Runnable() {
                    public void run() {
                        Object obj = parser.parse(new FileReader(tfile.getAbsolutePath()));
                        JsonObject jsonObject = (JsonObject) obj;
                        String documentname = obj.get("name").toString();
                        synchronized (map) {
                            List<String> paths = new ArrayList<String>();
                            //if key already exists append new path to the value
                            if (map.containsKey(documentname)) {
                                paths = map.get(documentname);
                            }
                            paths.add(tfile.getAbsolutePath());
                            map.put(documentname, paths);
                        }
                    }
                }));
            }

您可以嘗試替換此:

String documentname = obj.get("name").toString();
List<String> paths = new ArrayList<String>();
//if key already exists append new path to the value
if (map.containsKey(documentname)) {
    paths = map.get(documentname);
}
paths.add(tfile.getAbsolutePath());
map.put(documentname, paths);

有了這個:

String documentname = obj.get("name").toString();
List<String> paths = Collections.synchronizedList(new ArrayList<String>());
List<String> existing = map.putIfAbsent(documentname, paths);
if (existing != null) {
    paths = existing;
}
paths.add(tfile.getAbsolutePath());

如果兩個線程都試圖檢查(使用containsKey)然后將一個條目放入映射中,則putIfAbsent方法可避免出現競爭情況。

synchronizedList方法使用同步包裝器包裝嵌套集合,因此您無需同步對嵌套集合的訪問。 另外,您可以使用java.util.concurrent中的並發數據結構。

ThreadSafe是一個靜態分析工具,可發現並發錯誤。 它可以在Eclipse中獨立運行,也可以用作SonarQube插件。 特別是,它在您顯示的代碼中有一個檢查兩個錯誤的檢查器:

  • 非原子使用get-check-put(可以用putIfAbsent代替)( 示例報告
  • 並發集合中共享的非線程安全內容( 示例報告

我建議使用putIfAbsent編寫的代碼的一個問題是,它總是創建一個集合。 當映射已經包含給定鍵的條目時,將僅丟棄新集合。 但是,這可能對您的應用程序效率不高,並且可能對垃圾收集器施加額外的壓力。

因此,您可能需要考慮以下內容:

String documentname = obj.get("name").toString();
List<String> paths = map.get(documentname);
if (paths == null) {
   paths = Collections.synchronizedList(new ArrayList<String>());
   List<String> existing = map.putIfAbsent(documentname, paths);
   if (existing != null) {
       paths = existing;
   }
   paths.add(tfile.getAbsolutePath());
}

請注意此ThreadSafe 非原子的get-check-put鏈接中屏幕左下方的“規則描述”鏈接。 單擊“規則描述”鏈接,以獲取有關“獲取-檢查-放置”問題的進一步說明以及可能的解決方案。

ConcurrentHashMap是線程安全的,但這並不意味着您對ConcurrentHashMap所做的任何事情。 如果不使用同步,代碼中存在幾個問題:

  1. 您正在從多個線程同時訪問ArrayList。 ArrayList不是線程安全的
  2. 您正在containKey執行幾個操作( containKeygetput )。 這些操作都是線程安全的和原子的,但操作順序不是。 如果您需要該操作序列是原子的,則需要進行同步。 或者您需要使用等效的原子操作: putIfAbsent ()。

暫無
暫無

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

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