我想解析一个文本文件并计算一些令牌。文件逐行读取,每行分成标记。令牌放在一个列表中,然后由一个计算它们的方法处理。令牌存储在并发的散列映射中,其中令牌作为键,金额作为值。我还需要为最高的wordcount排序。
但看起来我错过了一些东西,因为我在计算上得到了不同的结果。
private ConcurrentHashMap<String, Integer> wordCount = new ConcurrentHashMap<>();
private ExecutorService executorService = Executors.newFixedThreadPool(4);
private void parseFile(String file) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file),
StandardCharsets.ISO_8859_1))) {
String line;
ArrayList<String> tokenListForThread;
while ((line = reader.readLine()) != null) {
tokenListForThread = new ArrayList<>();
StringTokenizer st = new StringTokenizer(line, " .,:!?", false);
while (st.hasMoreTokens()) {
tokenListForThread.add(st.nextToken());
}
startThreads(tokenListForThread);
}
reader.close();
executorService.shutdown();
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
printWordCount();
}
private void startThreads(ArrayList<String> tokenList) {
executorService.execute(() -> countWords(tokenList));
}
private void countWords(ArrayList<String> tokenList) {
for (String token : tokenList) {
int cnt = wordCount.containsKey(token) ? wordCount.get(token) : 0;
wordCount.put(token, cnt + 1);
/*if (wordCount.containsKey(token)){
wordCount.put(token, wordCount.get(token)+ 1 );
} else{
wordCount.putIfAbsent(token, 1);
}*/
}
}
private void printWordCount() {
ArrayList<Integer> results = new ArrayList<>();
for (Map.Entry<String, Integer> entry : wordCount.entrySet()) {
results.add(entry.getValue());
}
results.sort(Comparator.reverseOrder());
for (int i = 0; i < 10; i++) {
Integer tmp = results.get(i);
System.out.println(tmp);
}
}
我的错误在哪里,如果可能,我该如何解决?
答案 0 :(得分:0)
令牌计数增量应该是原子的,但它不是
int cnt = wordCount.containsKey(token) ? wordCount.get(token) : 0;
wordCount.put(token, cnt + 1);
两个具有相同令牌的线程&#39;在令牌列表中可以同时获得相同的cnt
,然后递增并放回。即总计数可能低于真实计数。
要修改它而不更改初始方法,您可以将AtomicInteger
用作wordCount
值
wordCount.putIfAbsent(token, new AtomicInteger());
wordCount.get(token).incrementAndGet();
第1步如果还没有token
,但您要添加它。令牌和zero
计数应该放在地图上。 putIfAbsent
方法是原子的,可以避免并发问题。
第2步获取AtomicInteger
的引用,该引用对应于给定的令牌并递增它。此操作也是线程保存。