37

What is the advantage of passing code directly to thread vs using CompletableFuture instead?

Thread thread = new Thread(() -> {do something});
thread.start();

VS

CompletableFuture<Void> cf1 =  
     CompletableFuture.runAsync(() -> {do something}); 
Sagar
  • 5,315
  • 6
  • 37
  • 66

3 Answers3

41

CompletableFuture.runAsync(...) runs the Runnable in the forkJoin-Pool which is managed, while new Thread() creates a new thread which you have to manage.

What does "is managed" mean, it's pre-allocated and the threads are shared in the JVM. When the runnable is completed, the thread can be reused for other runnables. This makes better usage of resources, especially as thread instantiation is an expensive operation - not only the object, but also some extra non-heap memory - the thread stack - has to be allocated.

Gerald Mücke
  • 10,724
  • 2
  • 50
  • 67
  • What else I have to if I go with the `thread.start` approach in terms of management? – Sagar Apr 16 '18 at 21:23
  • 4
    Re, "...which you have to manage." No. You don't _have_ to manage it. You can create a new thread, start it, and then forget about it. There's no penalty _except_ for the cost of creating and destroying the new thread. – Solomon Slow Apr 16 '18 at 21:44
  • @jameslarge Thanks! Never used threads directly, always used Executors, So wasn't aware of that. – Sagar Apr 16 '18 at 21:54
  • @James, you're right "have to manage" for any meaning of "manage", creation is already managing, but you could also interrupt it, giving it a name or put it into a thread group, and of course you could do all the deprecated and discouraged things such as `stop`,`suspend` or `resume. Of course, you don't _have_ to do any of it (apart from creation), but you could if you're keen on low-level thread handling (focus on "how" not "what"). For CompletableFuture, you can't because all is done for you and you operate on a high-level API for concurrency (focus on "what" not "how"). – Gerald Mücke Apr 17 '18 at 07:07
  • 1
    @jameslarge "There's no penalty except for the cost of creating and destroying", you're making it sound like everybody's got unlimited cores and running threads costs nothing. Well, it costs CPU time, which is one of the reasons to use pools – Cargeh Apr 17 '18 at 07:14
  • 5
    Some statements here seem to be a bit overgeneralized ( @Cargeh and james large). Threads are not only used to exploit the available processing resources (cores), but also to hide latencies or perform non-blocking operations - *even if the (new) threads are mainly waiting* (for IO, for example). There's no point in having a pool with `n` threads that are all waiting for something that a (non-existing) `n+1`th thread should do. Of course, such things are often hidden by abstraction layers and libraries. But creating a new thread manually *can* be perfectly fine - even if you're "experienced". – Marco13 Apr 17 '18 at 11:09
11

@Gerald Mücke already mentioned the important difference:

CompletableFuture.runAsync(...) runs the Runnable in the forkJoin-Pool which is managed, while new Thread() creates a new thread which you have to manage.

CompletableFuture will use threads managed by a ThreadPool (default or customized).

However, I think the following two points are also should be considered.

First

CompletableFuture has so many easily understandable methods to chain different asynchronous computations together, making it much easier to introduce asynchrony than directly using Thread.

CompletableFuture[] futures = IntStream.rangeClosed(0, LEN).boxed()
            .map(i -> CompletableFuture.supplyAsync(() -> runStage1(i), EXECUTOR_SERVICE))
            .map(future -> future.thenCompose(i -> CompletableFuture.supplyAsync(() -> runStage2(i), EXECUTOR_SERVICE)))
            .toArray(size -> new CompletableFuture[size]);
CompletableFuture.allOf(futures).join();

Second

You should never forget to handle exceptions; with CompletableFuture, you can directly handle them like this:

completableFuture.handle((s, e) -> e.toString()).join()

or take advantage of them this way to interrupt the computation:

completableFuture.completeExceptionally(new RuntimeException("Calculation failed!"));

while you will easily encounter some serious problems using Thread.

kevoroid
  • 5,052
  • 5
  • 34
  • 43
Hearen
  • 7,420
  • 4
  • 53
  • 63
1

CompletableFuture is a promise which uses default ForkJoinPool (thread pool of size equal to number of CPU's) unless provided with another ThreadPool. A thread pool will contain n or more number of Thread.

Shubham
  • 211
  • 2
  • 13