2015-06-19 51 views
1

對於同一組文件,每次運行的計數都會發生變化。 以下代碼仍然不符合數據。如何使線程安全?簡單的字數統計代碼。使用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"); 
    } 
} 

回答

1

併發容器確保內部一致性(例如,不添加相同的密鑰兩次),但它們無法保護存儲的值。現在你的代碼有競爭條件。另一個線程可以在您撥打get和致電replace之間遞增計數器。 replace然後將錯誤的值放入映射中,失去另一個線程執行的增量。

您需要使您的增量爲原子。事情是這樣的,它使用的replace確保在地圖中值的版本仍然是peforming替換之前相同:

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 
    } 
} 
+0

這works.Thanks多 – KDP

+0

能否請您讓我知道while循環的目的,在這裏? – KDP

+0

當競爭條件發生時,映射中的值由另一個線程在「putIfAbsent」和「replace」調用之間更改。這會導致替換返回'false'。循環將再次嘗試增量,並以新更新的值開始。 –