Пройдя длинный путь, чтобы изолировать мою проблему, удалось определить ее в следующих тестах. Все работает нормально, когда потоки имеют достаточно времени для завершения, но с настройкой ниже я иногда получаю пустые файлы. Это происходит примерно в 1/15 раза, когда я запускаю тест. Кажется, проблема связана с записью, т. Е. Файл на самом деле пуст.
import lombok.Synchronized;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.junit.jupiter.api.Assertions.assertEquals;
class Test{
File dummyFile=new File("/tmp/foo.txt"); //initially "foo"
String dummyText="foo";
@Test
void test() throws Exception{
assertEquals(dummyText,read(dummyFile));
spamAndTest();
for(int i = 0; i< 100 ; i++){
spamAndTestInThread(i);
}
Thread.sleep(100);
assertEquals(dummyText,read(dummyFile));
}
@Synchronized
public static String read(File f){
Charset charset = Charset.forName("UTF-8");
Path p = f.toPath();
String ret=null;
try {
ret = new String(Files.readAllBytes(p),charset);
} catch(Exception ex) {
ExUtils.handle(ex);
return null;
}
if(ret.trim().length()==0){
throw new RuntimeException("WTF, empty file:" +p);
}
return ret;
}
private void spamAndTestInThread(int i) {
Thread tt= new Thread("spam"){
public void run(){
for(int i = 0; i< 1000 ; i++){
save(dummyFile,dummyText);
}
}
};
tt.start();
}
@Synchronized
private void save(File file, String data) {
Charset charset = Charset.forName("UTF-8");
try {
Files.write(file.toPath(),data.getBytes(charset));
} catch (IOException e) {
e.printStackTrace()
}
}
}
Итак, все признаки указывают на то, что это вызвано ошибкой сохранения, когда JVM завершает работу. Я запускаю это на IntelliJ, но не думаю, что это так, поскольку оригинальная проблема также возникает в производстве, когда JVM завершается в неудачный момент. Кроме того, я не могу воспроизвести это, когда запущен только 1 поток.
Разве не должен Files.write обрабатывать завершения JVM из коробки? Если нет, то как с этим справиться?
Использование Oracle JDK 8 как локально, так и в продуктах.
РЕДАКТИРОВАТЬ:
Для пояснения, я не говорю о сбоях питания / сбоях JVM, но о случаях, когда отключение выполняется нормально. Также полезно отметить, что файл всегда полностью пуст, то есть он не умирает в середине записи.
EDIT2:
Я попробовал это с намного более длинной последовательностью и на этот раз заставил это обрезать середину и воспроизвел это только с 2 дополнительными нитями.
EDIT3:
Попытка добавления отключающих хуков и да, они запускаются после теста. Таким образом, можно ожидать, что эти записи смогут завершиться, но, по-видимому, нет.