[英]Data inconsistency using ConcurrentHashMap
對於同一組文件,每次運行的計數都會更改。 以下代碼仍與數據不一致。 如何使線程安全? 簡單的字數統計代碼。
package ConcurrentHashMapDemo;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
class FileReaderTask implements Runnable {
private String filePath;
private String fileName;
private ConcurrentMap<String, Integer> wordCountMap;
public FileReaderTask(String filePath, String fileName,
ConcurrentMap<String, Integer> wordCountMap) {
this.filePath = filePath;
this.fileName = fileName;
this.wordCountMap = wordCountMap;
}
public void run() {
File jobFile = new File(filePath + fileName);
try {
BufferedReader bReader = new BufferedReader(new FileReader(jobFile));
String line = "";
while ((line = bReader.readLine()) != null) {
String[] strArray = line.split(" ");
for (String str : strArray) {
if (wordCountMap.containsKey(str)) {
wordCountMap.replace (str.trim(),
wordCountMap.get(str.trim()) + 1);
} else {
wordCountMap.putIfAbsent(str.trim(), 1);
}
}
}
//Thread.sleep(10000);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class Main {
public static void main(String[] args) {
ConcurrentMap<String, Integer> wordCountMap = new ConcurrentHashMap<String, Integer>();
File fileDir = new File("c://job_files");
Thread[] threads = new Thread[fileDir.listFiles().length];
for(int i=0;i<threads.length;i++){
FileReaderTask frt = new FileReaderTask("c:/job_files/", fileDir.listFiles()[i].getName(), wordCountMap);
threads[i]= new Thread(frt);
threads[i].start();
}
//
for(int i=0;i<threads.length;i++){
try {
threads[i].join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for(Map.Entry<String, Integer> entry: wordCountMap.entrySet()){
String key = entry.getKey();
System.out.println(key +" - - "+wordCountMap.get(key));
}
System.out.println("Main");
}
}
並發容器可確保內部一致性(例如,不會兩次添加相同的密鑰),但是它們並不能保護存儲的值。 您的代碼現在處於競爭狀態。 另一個線程可以在get
調用和replace
調用之間增加計數器。 然后, replace
將錯誤的值放在映射中,從而丟失了另一個線程執行的增量。
您需要使增量原子化。 像這樣的東西,它使用replace
的版本, replace
確保在執行替換之前映射中的值仍然相同:
str = str.trim();
while(true) {
Integer oldValue = wordCountMap.putIfAbsent(str, 1);
if(oldValue != null) {
if(wordCountMap.replace(str, oldValue, oldValue + 1))
break; // Successfully incremented the existing count
} else {
break; // Added new count of 1
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.