Подтвердить что ты не робот

Темы и записи файлов

У меня есть Java-программа, которая использует 20 потоков. Каждый из них записывает свои результаты в файл с именем output.txt.

Я всегда получаю другое число строк в output.txt.

Может ли быть проблема с синхронизацией потоков? Есть ли способ справиться с этим?

4b9b3361

Ответ 1

может ли это быть проблемой синхронизации потоков?

Да.

Есть ли способ справиться с этим?

Да, убедитесь, что записи сериализованы путем синхронизации на соответствующем мьютексе. Или поочередно, есть только один поток, который фактически выводит в файл, и все остальные потоки просто записывают в очередь текст, который должен быть записан в очередь, из которой извлекается нить записи. (Таким образом, 20 основных потоков не блокируются при вводе/выводе.)

Повторите мьютекс: например, , если, все они используют один и тот же экземпляр FileWriter (или что-то еще), который я буду называть fw, тогда они могут использовать его как мьютекс:

synchronized (fw) {
    fw.write(...);
}

Если каждый из них использует свой собственный FileWriter или что-то еще, найдите что-то еще, что все они разделяют, чтобы быть мьютексом.

Но опять же, если поток, выполняющий ввод-вывод от имени других, вероятно, также является хорошим способом.

Ответ 2

Я предлагаю вам организовать его так: один поток-потребитель будет потреблять все данные и записывать их в файл. Все рабочие потоки будут передавать данные в поток потребителя синхронно. Или при написании нескольких потоков файлов вы можете использовать некоторые реализации мьютексов или блокировок.

Ответ 3

Если вам нужно какое-либо подобие производительности и простоты управления, перейдите в очередь производителей-потребителей и только один файловый писатель, как это предложил Алекс и другие. Разрешить все потоки в файле с мьютексом просто беспорядочно - каждая задержка на диск переносится непосредственно в главные функциональные возможности приложения (с дополнительным утверждением). Это особенно непримиримо с медленными сетевыми дисками, которые, как правило, уходят без предупреждения.

Ответ 4

В этом случае вы должны использовать синхронизацию. Представьте, что 2 потока (t1 и t2) одновременно открывают файл и начинают писать на него. Изменения, выполненные первым потоком, перезаписываются вторым потоком, потому что второй поток является последним, чтобы сохранить изменения в файле. Когда поток t1 записывает в файл, t2 должен ждать, пока t1 не закончит его задачу, прежде чем он сможет его открыть.

Ответ 5

Если вы можете сохранить файл как FileOutputStream, вы можете заблокировать его следующим образом:

FileOutputStream file = ...
....
// Thread safe version.
void write(byte[] bytes) {
  try {
    boolean written = false;
    do {
      try {
        // Lock it!
        FileLock lock = file.getChannel().lock();
        try {
          // Write the bytes.
          file.write(bytes);
          written = true;
        } finally {
          // Release the lock.
          lock.release();
        }
      } catch ( OverlappingFileLockException ofle ) {
        try {
          // Wait a bit
          Thread.sleep(0);
        } catch (InterruptedException ex) {
          throw new InterruptedIOException ("Interrupted waiting for a file lock.");
        }
      }
    } while (!written);
  } catch (IOException ex) {
    log.warn("Failed to lock " + fileName, ex);
  }
}

Ответ 6

Ну, без какой-либо детали реализации, это трудно понять, но, как показывает мой тестовый пример, я всегда получаю 220 строк вывода, т.е. постоянное количество строк, с FileWriter. Обратите внимание, что здесь не используется synchronized.

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
/**
 * Working example of synchonous, competitive writing to the same file.
 * @author WesternGun
 *
 */
public class ThreadCompete implements Runnable {
    private FileWriter writer;
    private int status;
    private int counter;
    private boolean stop;
    private String name;


    public ThreadCompete(String name) {
        this.name = name;
        status = 0;
        stop = false;
        // just open the file without appending, to clear content
        try {
            writer = new FileWriter(new File("test.txt"), true);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }


    public static void main(String[] args) {

        for (int i=0; i<20; i++) {
            new Thread(new ThreadCompete("Thread" + i)).start();
        }
    }

    private int generateRandom(int range) {
        return (int) (Math.random() * range);
    }

    @Override
    public void run() {
        while (!stop) {
            try {
                writer = new FileWriter(new File("test.txt"), true);
                if (status == 0) {
                    writer.write(this.name + ": Begin: " + counter);
                    writer.write(System.lineSeparator());
                    status ++;
                } else if (status == 1) {
                    writer.write(this.name + ": Now we have " + counter + " books!");
                    writer.write(System.lineSeparator());
                    counter++;
                    if (counter > 8) {
                        status = 2;
                    }

                } else if (status == 2) {
                    writer.write(this.name + ": End. " + counter);
                    writer.write(System.lineSeparator());
                    stop = true;
                }
                writer.flush();
                writer.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

Как я понимаю (и проверяю), в этом процессе есть две фазы:

  • все потоки в пуле, все созданные и запущенные, готовы захватить файл;
  • один из них захватывает его, а , я предполагаю, что он затем внутренне блокирует его, предотвращает доступ других потоков, потому что я никогда не вижу строки, объединенной из двух потоков. Поэтому, когда поток пишет, другие ждут, пока он завершит линию, и, скорее всего, выпустит файл. Итак, никаких условий гонки не произойдет.
  • Самый быстрый из остальных захватывает файл и начинает писать.

Ну, это точно так же, как толпа, ожидающая снаружи ванной, без очереди...

Итак, если ваша реализация отличается, покажите код, и мы можем помочь его сломать.