Синхронизация потоковой записи в файл с помощью синхронизаторов

#java #multithreading #synchronization #thread-safety

#java #многопоточность #синхронизация #безопасность потоков

Вопрос:

Я записываю в файл через несколько потоков, поэтому я использую следующий код:

             synchronized (this) {
                BufferedWriter bw = new BufferedWriter(
                        new FileWriter(path, true));
                bw.write(data);
            }
  

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

Спасибо

Комментарии:

1. Ну, вам нужно узнать, что представляет собой каждый из них, решить, какой из них использовать, и использовать их 😉 Боюсь, что ваш вопрос слишком широк — слишком много возможных ответов — на самом деле, на эту тему можно было бы написать книги. Отправная точка: java.util.concurrent JavaDocs

Ответ №1:

Честно говоря, гораздо лучший, почти тавтологический способ записи из нескольких источников одному и тому же автору — использовать (блокирующую) очередь. Таким образом, все потоки могут отбрасывать свои сообщения в очередь, а поток записи может выбирать сообщения из очереди и записывать их в файл. Как правило, это проще реализовать, а также более эффективно

— редактировать —

Пример:

 public class MyQueueableWriter implements Runnable {
  private BlockingQueue<Msg> q = new BlockingQueue();
  private FileOutputStream fis = ...;
  private volatile boolean running = true;

  public MyQueueableWriter(FileOutputStream fis) {
    this.fis = fis;
  }
  
  public void run() {
    try {
      while (running) {
        Message m = q.take();
        fis.write(...);
      }
      fis.close();
    } catch (IOException iox) {
      ...
    }
  }

  public void addMsg(Msg m) {
    q.put(m);
  }

  public void stop() {
    running = false;
  }
}
  

Затем добавление в очередь:

 public class EnqueueMsgRunnable implements Runnable {
  MyQueueableWriter q = ...;
  q.put(myMessage);
  q.put(myMessage2);
}
  

Затем просто

 for (int i =0; i < numSources; i  ) {
  EnqueueMsgRunnable r = new EnqueueMsgRunnable(...);
  new Thread(r).start();
}
  

Комментарии:

1. Хм, звучит интересно. Не могли бы вы показать мне простой пример того, как это реализовать, чтобы я мог попробовать это самостоятельно?

2. @Daniel в JavaDocs есть пример производителя-потребителя для BlockingQueue . В вашем случае потребитель будет записывать в файл, в то время как производители записывают в очередь.