Неожиданное поведение с CompletableFuture - PullRequest
0 голосов
/ 28 ноября 2018

Я пытаюсь создать простой пример с async 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;
    }
}

Когда я запускаю программу, она печатает это:

ВыборкаComptable

Запущен сон

Закончен сон

Процесс завершен с кодом выхода 0

, т. е. будущее никогда не завершится в выделенных 10 с.Так что здесь происходит?

Ответы [ 2 ]

0 голосов
/ 28 ноября 2018

Это потому, что вы не используете потокобезопасный тип данных, вы можете изменить свой код для использования 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;
        }
    }
0 голосов
/ 28 ноября 2018

Вы обращаетесь к valueIsSafe из нескольких потоков, вы должны определить эту переменную как volatile.

private volatile boolean valueIsSafe = false;

Использование ключевого слова volatile предотвратит кеширование этим значением потоков и заставит их читатьнеобработанная память при каждом доступе.

...