并发和并发数据结构

我正在练习一点并发。


public class WordOccurrencesBigFile {

    private String words;


    private ConcurrentHashMap<String, Pair<String, Integer>> wordOccurrencesMap = new ConcurrentHashMap<>();


    public WordOccurrencesBigFile(String wordsLine) {

        this.words = wordsLine;

    }


    public void processWords() {

        parseWordsLines();


        printOrderAlphabetically();

        printOrderByCount();

        printByInsertionOrder();

    }


    private void parseWordsLines() {

        String[] wordsLinesArray = words.split("\n");


        ExecutorService executor = Executors.newFixedThreadPool(5);

        for(String wordsLine: wordsLinesArray) {

            executor.execute(() -> parseWords(wordsLine));

        }


        executor.shutdown();

        while (!executor.isTerminated()) {

        }

        System.out.println("Finished all threads");

    }


    private void parseWords(String wordsLine) {

        System.out.println(Thread.currentThread().getName() + " Start.");

        System.out.println(Thread.currentThread().getName() + " Processing line: '" + wordsLine + "'");

        String[] wordsArray = wordsLine.split(" ");

}

}


在 parseWordsLines 上,使用 5 个线程池创建了一个 ExecutorService,并且 WordOccurrencesBigFile 类使用由“\n”创建的多行字符串实例化。目的是让每一行由不同的线程处理,并在 Map 上插入唯一单词的计数。


我期望通过使用 ConcurrentHashMap 足以处理我有多个线程读取和写入地图的事实。但是在我执行课程的大部分时间里,我得到了不同的计数。(奇怪的是主要是针对“bb”这个词。


但是添加了 synchronized(this) 问题就解决了。


有人可以解释为什么这种行为,解决这个问题的最佳方法,我应该将“this”传递给同步块或线程正在访问的对象吗?


人到中年有点甜
浏览 144回答 3
3回答

慕后森

ConcurrentHashMap 是线程安全的,可以保证每一个操作都是线程安全的。但是这些操作不是原子的:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (!wordOccurrencesMap.containsKey(word)) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pair = new Pair<>(word, 1);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //System.out.println(Thread.currentThread().getName() + " Creating Pair: " + pair);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pair = wordOccurrencesMap.get(word);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pair.setValue(pair.getValue() + 1);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; //System.out.println(Thread.currentThread().getName() + " Updating Pair: " + pair);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; wordOccurrencesMap.put(word, pair);您可以改为使用单个操作:wordOccurrencesMap.compute(word,&nbsp; &nbsp; &nbsp; &nbsp; (s, pair) -> pair == null ?&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new Pair<>(word, 1) : pair.setValue(pair.getValue() + 1));

蓝山帝景

好吧,添加可以synchronized(this)解决问题,但是您将失去多线程和并行化的所有好处。你需要的是 的computeIfAbsent方法ConcurrentMap。所以你的for循环体将转换为Pair<String, Integer> pair = wordOccurrencesMap.computeIfAbsent(word, w -> new Pair<>(w, 0));synchronized(pair) {&nbsp; &nbsp; pair.setValue(pair.getValue()+1);}现在你可以省略你的synchronized(this)块。编辑:但是您必须确保当第一个线程调用pair.setValue() 时,没有另一个线程可以调用pair.getValue(),如注释所述。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java