6

Hello I thought with CompletableFuture and the default ForkJoinPool I could optimize execution of task more than a classic ExecutorService but I missing something

With this code the execution takes 1 seconds, I have 3 worker threads:

for (int i = 0; i < 3; i++) {
    final int counter = i;
    listTasks.add(CompletableFuture.supplyAsync(() -> {
        Thread.sleep(1000);
        System.out.println("Looking up " + counter + " on thread " + Thread.currentThread().getName());
        return null;
    }));
}

OK, seems normal.

But with this code, it takes 3 seconds:

for (int i = 0; i < 9; i++) {
    final int counter = i;
    listTasks.add(CompletableFuture.supplyAsync(() -> {
        Thread.sleep(1000);
        System.out.println("Looking up " + counter + " on thread " + Thread.currentThread().getName());
        return null;
    }));
}

I thought that sleeping thread would be used to launch other waiting task, it should takes also 1 seconds. I've read for example that IO WAINTING thread state would mean that thread can be reused for other task. Can I test this behaviour with Thread.sleep()? Does my test method is wrong or did I understand something wrongly?

Andrew Lygin
  • 6,077
  • 1
  • 32
  • 37
bodtx
  • 590
  • 9
  • 29

2 Answers2

5

A sleeping thread cannot be used to do the job of another thread (especially, one thread cannot sleep for another thread). It's only CPU that can switch to the second thread when the first one goes to sleep.

When you provide a task to CompletableFuture.supplyAsync(), it goes to the default instance of ForkJoinPool that has as many threads as your computer has CPUs. You manually set allocated three threads to your default ForkJoinPool, so your nine tasks are distributed equally between them: each thread executes three tasks consecutively. So, you have three seconds as the result.

Andrew Lygin
  • 6,077
  • 1
  • 32
  • 37
  • Ok, thx for the sleep but If I do IO like file writing or webservice call instead of Thread.sleep, will I have the same result or does the Waiting thread will be reused to launch other IO/Web request? – bodtx Aug 26 '16 at 15:49
  • 1
    If your thread is freezed for one second to do some IO, you'll have absolutely the same effect. – Andrew Lygin Aug 26 '16 at 15:51
  • In your case, you have three threads and they pick available tasks from the shared queue. You put nine tasks into this queue. Each thread takes one task, executes it completely (sleeps for one second) and only then takes another task. But the processor at the same time (while your threads sleep) has a chance to switch to another threads (including threads of another processes) and do some useful work. – Andrew Lygin Aug 26 '16 at 15:55
  • I thought there was some optimisation with non blocking IO like [this](http://stackoverflow.com/questions/8546273/is-non-blocking-i-o-really-faster-than-multi-threaded-blocking-i-o-how) – bodtx Aug 26 '16 at 15:55
  • Non-blocking IO (NIO) has a different architecture than thread pools that you use in your code snippets. `Thread.sleep()` and simple IO is not a right choice to simulating non-blocking operations. If you used operations from `java.nio`, you could decrease the time, but using thread pools would be useless for that task. – Andrew Lygin Aug 26 '16 at 16:07
  • With NIO, your thread could submit some IO task (for instance, to write a buffer to a file) and continue running immediately to do some other useful work (I think that's what you called 'reuse for other task'). Later, it could check the result of the IO operation and decide what to do next. That's the difference between the conventional IO operations and NIO. – Andrew Lygin Aug 26 '16 at 16:17
1

Let's do the math. If you have 9 tasks and each task sleeps for 1 second and you have 2 processors you can only run 2 1-second sleeps at a time. Run this with 9 tasks and you get at least 3 seconds elapsed or at most most 4 seconds elapsed.

This isn't the same as non-blocking IO because this Runnable thread is CPU bound and will not give up the CPU until it completes (despite the sleep). If you look at things like RxJava's IO thread pool it creates one thread per task which is acceptable for IO tasks (but not CPU bound tasks).

John Vint
  • 39,695
  • 7
  • 78
  • 108
  • Agree with the math unless there was a possible context switching. But as you are saying and as RxJava seems to do with IO Thread Pool a thread cannot switch context even if it is blocked in IO. ok [this](http://stackoverflow.com/a/28402906/1077548) explains how it works with servlet, indeed there is no magic context switching, only ThreadPool for blocked thread – bodtx Aug 26 '16 at 16:11