0

Je veux analyser un fichier texte et compter quelques jetons. Le fichier est lu ligne par ligne, chaque ligne est divisée en jetons. Les jetons sont placés dans une liste et sont ensuite traités par une méthode qui les compte. Les jetons sont stockés dans une hashmap concurrente avec le jeton comme clé et le montant comme valeur. J'ai aussi besoin de trier cela pour le nombre de mots le plus élevé.Pourquoi existe-t-il encore une sorte de condition de concurrence dans ce code utilisant des hashmaps simultanés?

Mais il me semble qu'il me manque quelque chose, parce que j'ai des résultats différents sur le comptage.

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); 
    } 
} 

Où est mon erreur et si possible comment puis-je le réparer?

Répondre

0

comptage doit être Token incrémentation atomique, mais ce n'est pas

int cnt = wordCount.containsKey(token) ? wordCount.get(token) : 0; 
wordCount.put(token, cnt + 1); 

Deux fils avec les mêmes jetons dans la liste jeton peut obtenir le même cnt simultanément, puis incrémenter et remettre. Le nombre total peut être plus bas que le nombre réel.

Pour résoudre ce problème sans changer d'approche initiale, vous pouvez utiliser AtomicInteger comme wordCount valeurs

wordCount.putIfAbsent(token, new AtomicInteger()); 
wordCount.get(token).incrementAndGet(); 

Étape 1 Dans le cas où il n'y a pas encore token, mais vous allez l'ajouter. Jeton et zero compte devrait être mis à la carte. La méthode putIfAbsent est atomique, ce qui vous évite des problèmes simultanés.

Étape 2 Référence à AtomicInteger, qui correspond au jeton donné et l'incrémente. Cette opération est thread save soit.

+0

Merci, je ne savais pas à ce sujet. – Faulek