走了一条漫长的路来隔离我的问题,并设法将其确定为以下测试。当线程有足够的时间完成所有工作时,一切正常,但是在下面的设置中,我偶尔会遇到空文件。这种情况发生在我运行测试的时间的约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崩溃,而是在正常运行shutdownhooks的情况。同样要注意的是,该文件始终始终为空,即不会在写入过程中死亡。
EDIT2:
我用更长的字符串尝试了一下,这次又把它切成中间,并仅用了2个额外的线程来重现了它。
EDIT3:
尝试添加关闭挂钩,是的,它们在测试后正在运行。因此,希望这些写入能够完成,但显然不会。
答案 0 :(得分:0)
在测试方法中,您正在读取文件,然后再写入文件。如何确保虚拟文件将始终包含文本“ foo”。
assertEquals(dummyText,read(dummyFile));
spamAndTest();
您应该先在文件中写入文本,然后再读取if。检查更改此顺序是否可以解决您的问题。
spamAndTest();
assertEquals(dummyText,read(dummyFile));
您确定所有线程都能在100毫秒内成功执行吗?
Thread.sleep(100);
assertEquals(dummyText,read(dummyFile));
线程可能会截断文件,然后
当时assertEquals(dummyText,read(dummyFile));
被执行。
答案 1 :(得分:0)
设法通过添加以下关闭钩子最终解决了问题
Thread tt= new Thread(""){
public void run(){
//System.out.println("los shutdown hook (fileutils)");
Sleep.millis(100); //to give other hooks time to start saving
shutdown=true;
while(writing)
System.err.println("halting shutdown while write in progress");
}
};
Runtime.getRuntime().addShutdownHook(tt);
然后在保存方法的开始处设置Writing = True +在保存方法的末尾处设置write = false + if(shutdown)return;