java - 使用ConcurrentHashMap解决数据不一致问题

标签 java 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");
    }
}

最佳答案

并发容器确保内部一致性(例如,不会两次添加相同的 key ),但它们不采取任何措施来保护存储的值。您的代码现在存在竞争条件。另一个线程可以在调用 get 和调用 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
    }
}

关于java - 使用ConcurrentHashMap解决数据不一致问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30937750/

相关文章:

java - 如何在 Espresso 中重新运行失败的测试? - 头脑 Storm

java - 重命名后的文件长度

java - 简单、便宜的并发列表或集合?

java - 在这种情况下,ConcurrentHashMap 和 Synchronized Hash Map 哪个更好?

java - 保证键唯一时 HashMap 的性能

java - 如何处理 StaleElementReferenceException

java - Spring - 无法在 CrudRepository 中创建自定义查询

java - java中的多个json绑定(bind)

java - 从直方图计算平均值和百分位数?

java - 当两个线程同时执行cache.putIfAbsent时会发生什么?