CompletableFuture 的意外行为
Unexpected Behaviour with CompletableFuture
我正在尝试使用异步 CompletableFuture 创建一个简单示例,但我看到了一些奇怪的行为。我的想法是我启动 2 个异步期货,一个在设定时间后激活一个布尔标志,另一个轮询该标志以在线程 1 更改该标志后释放该值。这是我的代码:
package completablefutures;
import java.util.concurrent.CompletableFuture;
public class CFMain throws InterruptedException {
public static void main(String... args) {
CF cf = new CF();
CompletableFuture.supplyAsync(cf::getCompletable).thenRun(() -> System.out.println("Post-future action"));
CompletableFuture.supplyAsync(cf::doSleep);
Thread.sleep(10000);
}
}
和 CF class:
package completablefutures;
public class CF {
private boolean valueIsSafe = false;
public boolean getCompletable() {
System.out.println("Fetching completable");
while(true) {
if(this.valueIsSafe) {
System.out.println("Completable fetched");
return true;
}
}
}
public boolean doSleep() {
System.out.println("Started sleeping");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.valueIsSafe = true;
System.out.println("Finished sleeping");
return true;
}
}
当我让程序 运行 正常时,它会打印:
Fetching completable
Started sleeping
Finished sleeping
Process finished with exit code 0
即未来永远不会在分配的 10 秒内完成。那么这是怎么回事?
您正在从多个线程访问 valueIsSafe
,您必须将此变量定义为 volatile
。
private volatile boolean valueIsSafe = false;
使用 volatile 关键字将阻止线程缓存此值并强制它们在每次访问时读取原始内存。
这是因为您没有使用线程安全数据类型,您可以更改您的代码以使用 AtomicBoolean 这里是您使用 AtomicBoolean 的代码示例:
public class CF {
private AtomicBoolean valueIsSafe = new AtomicBoolean (false);
public boolean getCompletable() {
System.out.println("Fetching completable");
while(true) {
if(this.valueIsSafe.get()) {
System.out.println("Completable fetched");
return true;
}
//System.out.println("doing something");
}
}
public boolean doSleep() {
System.out.println("Started sleeping");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.valueIsSafe.set(true);
System.out.println("Finished sleeping");
return true;
}
}
我正在尝试使用异步 CompletableFuture 创建一个简单示例,但我看到了一些奇怪的行为。我的想法是我启动 2 个异步期货,一个在设定时间后激活一个布尔标志,另一个轮询该标志以在线程 1 更改该标志后释放该值。这是我的代码:
package completablefutures;
import java.util.concurrent.CompletableFuture;
public class CFMain throws InterruptedException {
public static void main(String... args) {
CF cf = new CF();
CompletableFuture.supplyAsync(cf::getCompletable).thenRun(() -> System.out.println("Post-future action"));
CompletableFuture.supplyAsync(cf::doSleep);
Thread.sleep(10000);
}
}
和 CF class:
package completablefutures;
public class CF {
private boolean valueIsSafe = false;
public boolean getCompletable() {
System.out.println("Fetching completable");
while(true) {
if(this.valueIsSafe) {
System.out.println("Completable fetched");
return true;
}
}
}
public boolean doSleep() {
System.out.println("Started sleeping");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.valueIsSafe = true;
System.out.println("Finished sleeping");
return true;
}
}
当我让程序 运行 正常时,它会打印:
Fetching completable
Started sleeping
Finished sleeping
Process finished with exit code 0
即未来永远不会在分配的 10 秒内完成。那么这是怎么回事?
您正在从多个线程访问 valueIsSafe
,您必须将此变量定义为 volatile
。
private volatile boolean valueIsSafe = false;
使用 volatile 关键字将阻止线程缓存此值并强制它们在每次访问时读取原始内存。
这是因为您没有使用线程安全数据类型,您可以更改您的代码以使用 AtomicBoolean 这里是您使用 AtomicBoolean 的代码示例:
public class CF {
private AtomicBoolean valueIsSafe = new AtomicBoolean (false);
public boolean getCompletable() {
System.out.println("Fetching completable");
while(true) {
if(this.valueIsSafe.get()) {
System.out.println("Completable fetched");
return true;
}
//System.out.println("doing something");
}
}
public boolean doSleep() {
System.out.println("Started sleeping");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.valueIsSafe.set(true);
System.out.println("Finished sleeping");
return true;
}
}