Другая конструкция с резьбой упростит поиск и устранение подобных проблем и позволит повысить эффективность сделки. Это длинный ответ, но вкратце: «если вы создаете потоки в Java, проверьте java.util.concurrent как можно скорее»).
Я полагаю, вы многопоточны в этом коде для изучения потоков, а не для ускорения подсчета слов, но это очень неэффективный способ использования потоков. Вы создаете два потока в строке - две тысячи потоков для файла из тысячи строк. Создание потока (в современных JVM) использует ресурсы операционной системы и, как правило, довольно дорого. Когда двум (не говоря уже о двух тысячах) потокам необходим доступ к общему ресурсу (например, к вашим счетчикам chars
и words
), результирующий конфликт памяти также снижает производительность.
Создание переменных счетчика synchronized
как Крис Кимптон предлагает или Atomic
, как WMR предлагает , вероятно, исправит код, но это также значительно усилит эффект раздора , Я почти уверен, что он будет работать медленнее, чем однопоточный алгоритм.
Я предлагаю иметь только один долгоживущий поток, который присматривает за chars
, и один для words
, каждый с рабочей очередью, в которую вы отправляете задания каждый раз, когда хотите добавить новый номер. Таким образом, только один поток записывает в каждую переменную, и если вы внесете изменения в проект, станет более очевидно, кто за что отвечает. Это также будет быстрее, потому что нет конфликта памяти и вы не создаете сотни потоков в тесном цикле.
Также важно, прочитав все строки в файле, дождаться завершения всех потоков, прежде чем вы фактически распечатаете значения счетчиков, иначе вы потеряете обновления из темы, которые еще не закончены. С вашим текущим дизайном вам нужно будет создать большой список созданных вами потоков и в конце пройти их, проверяя, что они все мертвы. С дизайном очереди и рабочего потока вы можете просто сказать каждому потоку опустошить свою очередь и затем подождать, пока это не будет сделано.
Java (от 1.5 и выше) делает этот вид дизайна очень простым для реализации: посмотрите java.util.concurrent.Executors.newSingleThreadExecutor . Это также упрощает добавление параллелизма позднее (при условии правильной блокировки и т. Д.), Поскольку вы можете просто переключиться на пул потоков, а не на отдельный поток.