Нужно ли блокировать файл, когда несколько потоков пытаются добавить контент с помощью NIO в JAVA? - PullRequest
2 голосов
/ 27 мая 2020

Сначала я создал пустой файл, а затем я вызвал какой-то поток для поиска в базе данных и получения содержимого результата, а затем добавил его в файл. Содержимое результата имеет тип String и может быть 20 МБ. Каждый поток должен записывать в файл по одному. Я много раз тестировал и обнаружил, что в блокировке нет необходимости. Это правильно? Общее количество строк в примере - 1000. Когда мне нужно добавить блокировку записи для работы с файлом?

    String currentName = "test.txt";
    final String LINE_SEPARATOR = System.getProperty("line.separator");
    ThreadPoolExecutor pool = new ThreadPoolExecutor(
            10, 100, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
    for (int i = 0; i < 500; i++) {
        pool.execute(() -> {
            try {
                appendFileByFilesWrite(currentName, "abc" +
                        ThreadLocalRandom.current().nextInt(1000) + LINE_SEPARATOR);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }

    IntStream.range(0, 500).<Runnable>mapToObj(a -> () -> {
        try {
            appendFileByFilesWrite( currentName,
                    "def" + ThreadLocalRandom.current().nextInt(1000) +
                    LINE_SEPARATOR);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }).forEach(pool::execute);

    pool.shutdown(); 

Вот метод:

public static void appendFileByFilesWrite(String fileName,String fileContent) throws IOException {
    Files.write(Paths.get(fileName), fileContent.getBytes(),StandardOpenOption.APPEND);
}

Ответы [ 2 ]

6 голосов
/ 27 мая 2020

Ответ: всегда .

Ваш тест работает на вас. Сейчас. Cегодня. Может быть, в полнолуние и не будет. Может быть, если вы купите новый компьютер, или обновите свою ОС, или обновите JDK, или вы проиграете песню Бритни Спирс в своем winamp, этого не произойдет.

SP c говорит, что Допустимо, чтобы запись распределялась по нескольким шагам, и поведение SOO.APPEND в этот момент не определено. Возможно, если вы напишете «Hello» и «World» одновременно, файл может содержать «HelWorllod». Вероятно, не будет. Но может.

Как правило, ошибки в параллелизме очень сложно (иногда буквально невозможно) проверить. Не делает это менее ошибкой; в основном вы получаете кучу отчетов об ошибках и отвечаете «не могу воспроизвести» на все из них. Это не лучшее место.

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

1 голос
/ 28 мая 2020

Вы можете использовать fileLock или просто добавить synchronized к методу.

while (true) {
  try {
  lock = fc.lock();
  break;
   } catch (OverlappingFileLockException e) {
    Thread.sleep(1 * 1000);
   }
 }
appendFileByFilesWrite( fileName, fileContent) ;

или просто изменить вот так:

public synchronized  static void appendFileByFilesWrite(String fileName,String fileContent) throws IOException {
    Files.write(Paths.get(fileName), fileContent.getBytes(),StandardOpenOption.APPEND);
}
...