具有CompletableFuture的意外行为

时间:2018-11-28 15:03:20

标签: java future completable-future

我正在尝试使用异步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类:

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;
    }
}

当我让程序运行时,它会显示以下内容:

  

可获取

     

开始睡觉

     

完成睡眠

     

以退出代码0结束的过程

即未来永远不会在分配的10秒内完成。那么这是怎么回事?

2 个答案:

答案 0 :(得分:3)

要从多个线程访问valueIsSafe,必须将此变量定义为volatile

private volatile boolean valueIsSafe = false;

使用volatile关键字将防止线程缓存此值,并强制它们在每次访问时读取原始内存。

答案 1 :(得分:1)

这是因为您没有使用线程安全的数据类型,可以更改代码以使用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;
        }
    }