Java8; Utilize sleep time on one thread, but multiple callables

Viewed 519

Is it possible in standard java8 to execute multiple callables on single thread concurrently?
i.e. when one callable sleeps, start working on other callable.

My current experiment, which does not work:

    ExecutorService executor = Executors.newSingleThreadExecutor();
    List<Future> fs = new ArrayList<>();
    for (int i = 0; i < 2; i++) {
        final int nr = i;
        fs.add(executor.submit(() -> {
            System.out.println("callable-" + nr + "-start");
            try { Thread.sleep(10_000); } catch (InterruptedException e) { }
            System.out.println("callable-" + nr + "-end");
            return nr;
        }));
    }
    try { executor.awaitTermination(5, TimeUnit.SECONDS); } catch (InterruptedException e) { }

Results in:

callable-0-start
callable-0-end
callable-1-start
callable-1-end

I want to have:

callable-0-start
callable-1-start
callable-0-end
callable-1-end

Notes:

  • I kind of expect an answer: "No it's not possible. This is not how threads work. Once thread is assigned to some executable code it runs until completion, exception or cancellation. There can be no midflight-switching between callables/runnables. Thread.sleep only allows other threads to run on CPU/core." (explicit confirmation would put my mind to rest)
  • Naturally, this is "toy" example.
  • This is about understanding, not some specific problem that I have.
2 Answers

What you attempt to do is to emulate deprecated functionality from older java versions. Back then it was possible to stop, suspend or resume a Thread. But from the javadoc of Thread.stop:

This method is inherently unsafe. Stopping a thread with Thread.stop causes it to unlock all of the monitors that it has locked (as a natural consequence of the unchecked ThreadDeath exception propagating up the stack). If any of the objects previously protected by these monitors were in an inconsistent state, the damaged objects become visible to other threads, potentially resulting in arbitrary behavior. Many uses of stop should be replaced by code that simply modifies some variable to indicate that the target thread should stop running. The target thread should check this variable regularly, and return from its run method in an orderly fashion if the variable indicates that it is to stop running. If the target thread waits for long periods (on a condition variable, for example), the interrupt method should be used to interrupt the wait.

As described by this outtake, the risks of doing what you want were critical, and therefore this behavior has been deprecated.

I would suggest, that instead of trying to force a running thread into some sort of halting position from the outside, you should maybe think about a ThreadPool API that allows you to package your code segments properly, so that their state can be unloaded from a thread, and later resumed. e.g. create Ticket, which would be an elementary job, which a thread would always complete before beginning another, a TicketChain that sequentially connects tickets and stores the state. Then make a handler that handles tickets one by one. In case a Ticket cannot be currently done (e.g. because not all data is present, or some lock cannot be acquired) the thread can skip it until a later point in time, when said conditions might be true.

Building on answer from @TreffnonX

One way to achieve desired stdout result is using CompletableFuture
(callable code must be explicitly split into separate functions):

    ExecutorService executor = Executors.newSingleThreadExecutor();
    CompletableFuture<Integer>[] fs = new CompletableFuture[2];
    for(int i=0; i<2; i++) {
        final Integer ii = i;
        fs[i] = (CompletableFuture.completedFuture(ii)
                .thenApply((Integer x) -> { System.out.println("callable-" + x + "-start");return x; })
                .thenApplyAsync((Integer x) -> { try { Thread.sleep(1_000); } catch (InterruptedException e) {Thread.currentThread().interrupt();} return x; }, executor)
                .thenApply((Integer x) -> { System.out.println("callable-" + x + "-end");return x; }));
    }
    CompletableFuture.allOf(fs).join();
    try { executor.awaitTermination(5, TimeUnit.SECONDS); } catch (InterruptedException e) { }

Result:

callable-0-start
callable-1-start
callable-0-end
callable-1-end
Related