【发布时间】:2021-10-06 16:14:05
【问题描述】:
主要代码
我有一种方法使用服务来推送作业并等待此类服务在某处创建文件。
这样的服务公开了两种方法:
-
String pushJob(...) -
boolean isJobReady(String id)true,否则返回false
为了等待,这个方法做了这样的事情:
CompletableFuture<Void> waiter = new CompletableFuture<>();
waitFor(waiter, jobId); //<-- this will poll on the isJobReady endpoint until it responds with true
// when this happens, it will complete the future.
waiter.get(1, TimeUnit.HOUR); //<-- wait for the job to complete within the hour
//do something with the file that was created once the get returns without exceptions
单元测试
这很好用,但我想测试一下。因此,我做了一个服务的模拟实现,公开了pushJob 和isJobReady 两种方法:
public class MockService implements Service {
private volatile boolean isReady;
@Override
public String pushJob() {
isReady = false;
CompletableFuture.runAsync(() -> {
Thread.sleep(10000); //<-- simulates the time spent to perform the job on service side
File file = new File("...");
if (file.createNewFile()) {
isReady = true;
} else {
throw new IllegalStateException("Could not create test file");
}
});
return "12345";
}
@Override
public boolean isJobReady(String id) {
return isReady;
}
}
然后我在我的代码中注入这样的模拟实现以对其进行测试。
问题
mock 实现创建文件后,将 volatile 字段 isReady 设置为 true,因此当主代码看到它时,它会立即去搜索创建的文件。
但是,它会在两次中找到一次。
如果我在搜索文件之前设置断点并等待一秒钟,代码将系统地成功。 但是,如果我以正常的执行速度(或简单的编译时间)进行测试,测试就会变得非常不稳定。
我发现这是一个多线程问题,但我真的不明白这是怎么可能的,因为测试代码不会设置变量 isReady = true,直到它得到来自 createNewFile() 的响应(根据到javadoc)Atomically creates a new, empty file named by this abstract pathname if and only if a file with this name does not yet exist.
为什么会发生这种情况?如何正确同步文件系统,以便在模拟服务中创建文件在主代码上正确可见?
【问题讨论】:
标签: java multithreading file concurrency