[英]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插件。 特別是,它在您顯示的代碼中有一個檢查兩個錯誤的檢查器:
我建議使用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所做的任何事情。 如果不使用同步,代碼中存在幾個問題:
containKey
執行幾個操作( containKey
, get
, put
)。 這些操作都是線程安全的和原子的,但操作順序不是。 如果您需要該操作序列是原子的,則需要進行同步。 或者您需要使用等效的原子操作: putIfAbsent
()。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.