[英]Will java streams sum values of a ConcurrentHashMap in an consistent manner?
[英]Java ConcurrentHashMap corrupt values
我有一個ConcurrentHashMap,偶爾表現出奇怪的行為。
當我的應用程序第一次啟動時,我從文件系統中讀取一個目錄,並使用文件名作為鍵將每個文件的內容加載到ConcurrentHashMap中。 某些文件可能為空,在這種情況下,我將值設置為“空”。
一旦加載了所有文件,工作線程池將等待外部請求。 當請求進來時,我調用getData()函數,在那里我檢查ConcurrentHashMap是否包含密鑰。 如果密鑰存在,我得到值並檢查值是否為“空”。 如果value.contains(“empty”),我返回“找不到文件”。 否則,返回文件的內容。 當密鑰不存在時,我嘗試從文件系統加載文件。
private String getData(String name) {
String reply = null;
if (map.containsKey(name)) {
reply = map.get(name);
} else {
reply = getDataFromFileSystem(name);
}
if (reply != null && !reply.contains("empty")) {
return reply;
}
return "file not found";
}
有時,ConcurrentHashMap將返回非空文件的內容(即value.contains("empty") == false
),但是行:
if (reply != null && !reply.contains("empty"))
返回FALSE。 我將IF語句分為兩部分: if (reply != null)
和if (!reply.contains("empty"))
。 IF語句的第一部分返回TRUE。 第二部分返回FALSE。 所以我決定打印出變量“reply”以確定字符串的內容是否確實包含“empty”。 這不是這種情況,即內容不包含字符串“empty”。 此外,我添加了這條線
int indexOf = reply.indexOf("empty");
由於變量回復在我打印出來時不包含字符串“empty”,因此我希望indexOf
返回-1。 但是函數返回的值大約是字符串的長度,即if reply.length == 15100
,則reply.indexOf("empty")
返回15099。
我每周都會遇到這個問題,大約每周2-3次。 此過程每天重新啟動,因此會定期重新生成ConcurrentHashMap。
有沒有人在使用Java的ConcurrentHashMap時看到過這種行為?
編輯
private String getDataFromFileSystem(String name) {
String contents = "empty";
try {
File folder = new File(dir);
File[] fileList = folder.listFiles();
for (int i = 0; i < fileList.length; i++) {
if (fileList[i].isFile() && fileList[i].getName().contains(name)) {
String fileName = fileList[i].getAbsolutePath();
FileReader fr = null;
BufferedReader br = null;
try {
fr = new FileReader(fileName);
br = new BufferedReader(fr);
String sCurrentLine;
while ((sCurrentLine = br.readLine()) != null) {
contents += sCurrentLine.trim();
}
if (contents.equals("")) {
contents = "empty";
}
return contents;
} catch (Exception e) {
e.printStackTrace();
if (contents.equals("")) {
contents = "empty";
}
return contents;
} finally {
if (fr != null) {
try {
fr.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (br != null) {
try {
br.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (map.containsKey(name)) {
map.remove(name);
}
map.put(name, contents);
}
}
}
} catch (Exception e) {
e.printStackTrace();
if (contents.equals("")) {
contents = "empty";
}
return contents;
}
return contents;
}
我認為你的問題是你的一些操作應該是原子的,而不是。
例如,一種可能的線程交錯場景如下:
線程1在getData
方法中讀取此行:
if (map.containsKey(name)) // (1)
結果為false並且線程1進入
reply = getDataFromFileSystem(name); // (2)
在getDataFromFileSystem
,您有以下代碼:
if (map.containsKey(name)) { // (3) map.remove(name); // (4) } map.put(name, contents); // (5)
想象另一個線程(線程2)到達(1)
而線程1在(4)
和(5)
:名稱不在地圖中,因此線程2再次轉到(2)
現在,這並沒有解釋您正在觀察的具體問題,但它說明了當您讓許多線程在沒有同步的代碼段中並發運行時,可能並且確實發生了奇怪的事情。
就目前而言,我無法找到您描述的場景的解釋,除非您在測試中多次調用reply = map.get(name)
,在這種情況下,2次調用很可能不會返回同樣的結果。
首先,甚至不要認為ConcurrentHashMap
中存在錯誤。 JDK故障是非常罕見的,甚至有趣的想法將使您遠離正確調試代碼。
我認為您的錯誤如下。 由於您使用的是contains("empty")
,如果文件中的行中包含單詞"empty"
,會發生什么? 這不是搞砸了嗎?
我會使用==
而不是使用contains("empty")
。 將“empty”設為private static final String
然后就可以使用相等的。
private final static String EMPTY_STRING_REFERENCE = "empty";
...
if (reply != null && reply != EMPTY_STRING_REFERENCE) {
return reply;
}
...
String contents = EMPTY_STRING_REFERENCE;
...
// really this should be if (contents.isEmpty())
if (contents.equals("")) {
contents = EMPTY_STRING_REFERENCE;
}
這是,順便說一下,你應該使用==
來比較字符串。 在這種情況下,您希望通過引用而不是內容來測試它,因為文件中的行實際上可能包含魔術字符串。
以下是其他一些觀點:
String
,就應該將其拉到static final
字段。 Java無論如何都可能會為你做這件事,但它也使代碼更加清晰。 當你對ConcurrentHashMap
進行2次調用時,@ iscylias會對競爭條件有所了解。 例如,而不是做:
if (map.containsKey(name)) { reply = map.get(name); } else {
你應該做以下事情,所以你只做一個。
reply = map.get(name); if (reply == null) {
在您的代碼中,您執行此操作:
if (map.containsKey(name)) { map.remove(name); } map.put(name, contents);
這應該改寫如下。 沒有必要在引入種族條件的看跌期權之前移除,如提到的@assylias。
map.put(name, contents);
你說:
如果reply.length == 15100,則reply.indexOf(“empty”)返回15099。
使用相同的reply
字符串無法做到這一點。 我懷疑你正在尋找不同的線程或以其他方式誤解輸出。 同樣,不要誤以為java.lang.String
中存在錯誤。
首先,如果您按順序從多個線程調用其方法,則使用ConcurrentHashMap
不會保護您。 如果你調用containsKey
並get
之后get
另一個線程調用remove
之間你將得到一個null結果。 一定要只調用get並檢查null而不是containsKey / get。 它在性能方面也更好,因為兩種方法幾乎都有相同的成本。
其次,奇怪的indexOf調用結果要么是由於編程錯誤,要么是指向內存損壞。 您的應用程序中是否包含任何本機代碼? 你在getDataFromFileSystem
做什么? 我在使用多個線程的FileChannel
對象時發現內存損壞。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.