Использование поставщика в CompletableFuture дает другой результат, чем использование лямбда

Я создал небольшой пример чтения текстового файла и заключил вызов в CompletableFuture,

public class Async {
    public static void main(String[] args) throws Exception {
        CompletableFuture<String> result = ReadFileUsingLambda(Paths.get("path/to/file"));
        result.whenComplete((ok, ex) -> {
            if (ex == null) {
                System.out.println(ok);
            } else {
                ex.printStackTrace();
            }
        });
    }

    public static CompletableFuture<String> ReadFileUsingSupplier(Path file) throws Exception {
        return CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override
            public String get() {
                try {
                    return new String(Files.readAllBytes(file));
                } catch (IOException e) {
                    e.printStackTrace();
                    return "test";
                }
            }
        }, ForkJoinPool.commonPool());
    }

    public static CompletableFuture<String> ReadFileUsingLambda(Path file) throws Exception {
        return CompletableFuture.supplyAsync(() -> {
            try {
                return new String(Files.readAllBytes(file));
            } catch (IOException e) {
                e.printStackTrace();
                return "test";
            }
        } , ForkJoinPool.commonPool());
    }
}

Этот код ничего не возвращает. Выполняется и "ничего не происходит", без ошибок или вывода. Если я позвоню ReadFileUsingSupplier вместо ReadFileUsingLambda тогда я получаю содержимое файла, напечатанное в консоли!

Для меня это не имеет смысла, потому что лямбда-это сокращение для написания встроенной функции, и это не должно изменить поведение, но в этом примере это очевидно.

2 ответа

Решение

Я думаю, что это просто вопрос времени выполнения - лямбда может занять немного больше времени для выполнения, что позволит программе завершиться до того, как вы закончите читать файл.

Попробуй это:

  • добавить Thread.sleep(1000); как первый оператор в блоке try в ReadFileUsingSupplier и вы не увидите никакого выхода
  • добавить Thread.sleep(1000); в конце вашего основного при использовании ReadFileUsingLambda и вы увидите ожидаемый результат

Чтобы убедиться, что ваш основной не выйдет до завершения будущего, вы можете позвонить:

result.join();

Как уже отмечалось, в любом случае вы должны использовать result.join(), чтобы избежать слишком быстрого выхода из основного потока.

Кажется, есть штраф за использование лямбда-выражений против анонимных замыканий, когда JVM нагревается, после чего производительность остается той же. Я нашел эту информацию в другом SO-потоке, который, в свою очередь, связывает исследование производительности с Oracle.

Как sidenote, Thread.sleep () не очень хорошая идея, когда-либо исправлять странные проблемы с синхронизацией. Выяснение причины и применение соответствующих мер будет намного понятнее, если вы перечитываете вас или других, например,

System.out.println(result.get(5, TimeUnit.SECONDS));

Это также позволяет отказаться от.join ().

Другие вопросы по тегам