#java #multithreading #file-io #io
#java #многопоточность #file-io #io
Вопрос:
Я столкнулся с проблемой, когда у меня есть несколько потоков, которые записывают в один и тот же PrintWriter, и не все данные записываются в файл. Я знаю, что многопоточная часть работает правильно, поскольку я могу распечатать все на консоль. Синхронизация операторов записи, похоже, не работает. В чем может быть проблема?
ExecutorService pool = Executors.newFixedThreadPool(poolSize);
for (Integer i : map.keySet()) {
final Collection<String[]> set = map.get(i);
pool.submit(new Runnable() {
public void run() {
StringBuffer sb = Matcher.performCollectionMatch(params);
synchronized (this) {
resultFile.print(sb); //this is a PrintWriter - it does NOT capture all sb
resultFile.flush();
System.out.print(sb); //this actually prints out ALL sb
}
}
});
} //FOR loop
Комментарии:
1. достаточно ли велик размер вашего пула?
2. Я предполагаю, что это так. Я установил его на 10 потоков, и у меня не закончилась память. Кроме того, я знаю, что потоки выполняют работу правильно, поскольку я могу получать выходные данные из System.out, и это точно.
Ответ №1:
Чтобы синхронизация работала, вы должны использовать один и тот же объект для всех потоков, например:
...
synchronized (resultFile) {
...
Комментарии:
1. Изначально это именно то, что я пробовал (мое понимание синхронизированных операторов). Однако он записал в файл еще меньше результатов.
2. Тогда что-то еще совсем не так. Этот ответ правильно исправляет синхронизацию здесь, которая фактически не защищала доступ к потоку.
Ответ №2:
Вы закрываете PrintWriter
после остановки пула?
pool.shutdown();
final boolean terminated = pool.awaitTermination(8, TimeUnit.SECONDS);
if (!terminated) {
throw new IllegalStateException("pool shutdown timeout");
}
resultFile.close();
Комментарии:
1. Чего я не понимал, так это того, что pool.shutdown() не блокируется до завершения всех потоков. Простое исправление: ` pool.shutdown(); в то время как (!pool.isTerminated) { } resultFile.close(); `
2. Это
while
требует много ресурсов процессора.awaitTermination
делает то же самое, но использует 0% процессора.3. Спасибо за совет. Будет соответствующим образом изменено.
Ответ №3:
Более простым решением является обеспечение наличия только одного потока в пуле. Таким образом, вам не нужно синхронизировать запись, поскольку существует только один поток.
ExecutorService pool = Executors.newSingleThreadedPool();
for (Integer i : map.keySet()) {
final Collection<String[]> set = map.get(i);
pool.executor(new Runnable() {
public void run() {
StringBuilder sb = Matcher.performCollectionMatch(params);
resultFile.print(sb); //this is a PrintWriter - it does NOT capture all sb
System.out.print(sb); //this actually prints out ALL sb
}
});
} //FOR loop
Вполне вероятно, что горлышко бутылки — это ваш доступ к диску, поэтому добавление дополнительных потоков может не помочь.
Комментарии:
1. @kirdie ИМХО, цель многопоточности — максимизировать производительность, а не использовать все имеющиеся у меня ядра. Часто оптимальное количество потоков равно одному. 😉