Предотвратить удаление текстового файла при доступе из нескольких потоков - PullRequest
0 голосов
/ 05 марта 2019

Я пытаюсь отладить проблему, которая только что появилась в моей программе.До сих пор я без проблем писал, читал и обновлял файл props, используя следующую структуру кода:

public void setAndReplacePropValue(String dir, String key, String value) throws FileNotFoundException, IOException {

    if (value != null) {
     File file = new File(dir);
     if (!file.exists()) {
                System.out.println("File: " + dir + " is not present. Attempting to create new file now..");
                new FilesAndFolders().createTextFileWithDirsIfNotPresent(dir);
     }

     if (file.exists()) {
        try {
            FileInputStream fileInputStream = null;
            fileInputStream = new FileInputStream(file);
            if (fileInputStream != null) {
                Properties properties = new Properties();
                properties.load(fileInputStream);
                fileInputStream.close();

              if (properties != null) {
               FileOutputStream fileOutputStream = new FileOutputStream(file);
                properties.setProperty(key, value);
                properties.store(fileOutputStream, null);
                fileOutputStream.close();
                }
            }   
        }
        catch (Exception e) {
         e.printStackTrace();
        }
    } else {
            System.out.println("File: " + dir + " does not exist and attempt to create new file failed");
            }
        }
    }

Однако недавно я заметил, что это конкретный файл (назовем его: C:\\Users\\Admin\\Desktop\\props.txt)удаляется после обновления из нескольких потоков.Я не уверен в точном источнике этой ошибки, так как она кажется случайной.

Я подумал, что, возможно, если два потока вызывают setAndReplacePropValue(), а первый поток вызывает FileOutputStream fileOutputStream = new FileOutputStream(file); до того, как он получитшанс перезаписать данные в файл (через properties.store(fileOutputStream, null)), тогда второй поток может вызвать fileInputStream = new FileInputStream(file); для пустого файла - что приведет к удалению предыдущего потока предыдущими данными при записи «пустых» данных обратно в файл.

Чтобы проверить свою гипотезу, я попытался вызвать setAndReplacePropValue() из нескольких потоков от нескольких сотен до тысячи раз подряд, внося изменения в setAndReplacePropValue() по мере необходимости.Вот мои результаты:

  1. Если setAndReplace() объявлен как static + synchronized, исходные данные props сохраняются.Это остается верным, даже когда я добавляю случайную задержку после вызова FileOutputStream - до тех пор, пока JVM существует нормально.Если JVM уничтожена / прервана (после вызова FileOutputStream), предыдущие данные будут удалены.

  2. Если я удаляю оба модификатора static и synchronized из setAndReplace() извоните setAndReplace() 5000 раз, старые данные все еще сохраняются ( почему? ) - до тех пор, пока JVM завершается нормально.Это похоже на правду, даже когда я добавляю случайную задержку в setAndReplace() (после вызова FileOutputStream).

  3. Когда я пытаюсь изменить файл реквизита с помощью ExecutorService (иногда я получаю доступ к setAndReplacePropValue() через ExecutorService в моей программе), содержимое файла сохраняется до тех пор, пока нет задержки после FileOutputStream.Если я добавлю задержку, и задержка будет> значением «тайм-аута», установленным в future.get () (поэтому выбрасывается interrupted exception), данные сохраняются NOT .Это остается верным, даже если я добавлю static + synchronized ключевых слов к методу.

Короче говоря, мой вопрос: каково наиболее вероятное объяснение того, почему файл удаляется?(Я думал, что пункт 3 может объяснить ошибку, но на самом деле я не sleeping после вызова new FileOutputStream(), так что, вероятно, это не помешает записи данных обратно в файл после вызова new FileOutputStream()). Есть ли еще одна возможность, о которой я не подумал?

Кроме того, почему point 2 true?Если метод не объявлен как статический / синхронизированный, разве это не должно привести к тому, что один поток создаст InputStream из пустого файла?Спасибо.

1 Ответ

0 голосов
/ 06 марта 2019

К сожалению, очень сложно предоставить отзыв о вашем коде без тонны дополнительной информации, но, надеюсь, мои комментарии будут полезны.

В общем случае чтение и запись нескольких потоков из одного и того же файладействительно плохая идея.Я не могу согласиться с тем, что @ Hovercraft-Full-Of-Eels рекомендует, чтобы у вас была тема 1 для чтения / записи, а другие темы просто добавляли обновления в общую BlockingQueue.

Но здесь говорится о некоторых комментариях.

Если setAndReplace () объявлен как статический + синхронизированный, исходные данные реквизита сохраняются.

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

Если JVM уничтожена / прервана (после вызова FileOutputStream)тогда предыдущие данные будут удалены.

Я не совсем понимаю эту часть, но у вашего кода должны быть хорошие предложения try / finally, чтобы убедиться, что файлы закрываются надлежащим образом, когда JVM завершает работу.Если JVM жестко уничтожена, файл может быть открыт, но еще не записан (в зависимости от времени).В этом случае я бы порекомендовал вам записать во временный файл и переименовать в файл свойств, который является атомарным.Тогда вы можете пропустить обновление, если JVM уничтожена, но файл никогда не будет перезаписан и будет пустым.

Если я удаляю как статические, так и синхронизированные модификаторы из setAndReplace () и вызываю setAndReplace () 5000 разстарые данные все еще сохраняются (почему?)

Понятия не имею.Зависит от условий гонки.Возможно, вам просто повезло.

Когда я пытаюсь изменить файл props с помощью ExecutorService (я иногда обращаюсь к setAndReplacePropValue () через ExecutorService в моей программе), содержимое файла сохраняется до тех пор, пока после FileOutputStream не будет задержки,Если я добавлю задержку, и задержка будет> значением «тайм-аута», установленным в future.get () (так, чтобы генерировалось прерванное исключение), данные НЕ сохраняются.Это остается верным, даже если я добавлю статические + синхронизированные ключевые слова к методу.

Я не могу ответить на этот вопрос, не увидев определенный код.

На самом деле это было бы хорошей идеей, если выимел фиксированный пул потоков с 1 потоком, тогда каждый из потоков, которые хотят обновить значение, просто отправлял бы объект поля / значения в пул потоков.Это примерно то, о чем говорил @ Hovercraft-Full-Of-Eels.

Надеюсь, это поможет.

...