3

I have created a small example of reading a text file and wrap the call with 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());
    }
}

This code returns nothing. It executes and "nothing happens", no errors or output. If I call ReadFileUsingSupplier instead of ReadFileUsingLambda then I get the file content printed in the console!

To me this doesn't make sense because a lambda is a shorthand for writing an inline function and it shouldn't change the behaviour but in this example it does apparently.

Stefan Dollase
  • 4,530
  • 3
  • 27
  • 51
Sul Aga
  • 6,142
  • 5
  • 25
  • 37

2 Answers2

4

I think it's just a matter of execution timing - the lambda may take a little more to execute, allowing the program to exit before you are done reading the file.

Try this:

  • add a Thread.sleep(1000); as the first statement within the try block in ReadFileUsingSupplier and you won't see any output
  • add a Thread.sleep(1000); at the end of your main when using ReadFileUsingLambda and you will see the expected output

To make sure your main doesn't exit before the future is completed, you can call:

result.join();
assylias
  • 321,522
  • 82
  • 660
  • 783
  • thank you. This did the trick but going forward how can I make sure that my program doesn't exit until I get the result back from CompletableFuture – Sul Aga Dec 27 '16 at 10:49
  • 2
    @SulAga You can simply call `result.get()` or `result.join()` – assylias Dec 27 '16 at 10:51
  • So the join or get is a blocking call? is it a blocking on the main thread or on CompletableFuture thread? is this a problem i.e. blocking – Sul Aga Dec 27 '16 at 11:10
  • 1
    @SulAga It's blocking on the thread from which it's called - if you just add `result.join()` at the end of `main`, it will block the main thread. – assylias Dec 27 '16 at 11:11
2

As noted, you need to result.join() in either case to avoid the main thread exiting too quickly.

It seems that there's a penalty for using lambdas vs anonymous closures while the JVM warms up, thereafter the performance is the same. I found this information at on another SO thread - which in turn links a performance study by Oracle.

As a sidenote it's not a great idea to Thread.sleep() to fix weird timing issues, ever. Figuring out the cause and applying the appropriate measures would be much clearer when re-read by you or by others, e.g.

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

This enables you to ditch the .join(), too.

Community
  • 1
  • 1
ahri
  • 351
  • 2
  • 11