How Java maintains the future task result, if the child thread finished before calling get method

1.2k views Asked by At

I am running below program, where Main thread finishes 1st, before calling the get method of the child callable thread.

public class Main {
    public static void main(String[] args) throws InterruptedException, ExecutionException {        
        ExecutorService exec =  Executors.newFixedThreadPool(1);
        Future<String> f =  exec.submit(new TThread());
        Thread.sleep(10000);
        System.out.println("Main Thread "+System.currentTimeMillis());      
        String s  = (String) f.get();
        System.out.println(s);
        exec.shutdown();
    }
}

class TThread implements Callable<String>{
    @Override
    public String call() throws Exception{
        Thread.sleep(3000);
        System.out.println("TThread Thread "+System.currentTimeMillis());
        return "something";
    }
}

The output is

TThread Thread 1497248667516
Main Thread 1497248674515
something

My expectation was get() might return null, because TThread thread was completed much earlier than to get() call. Does this mean that Java maintains the result from callable irrespective of the caller is calling the get() or not. Also can somebody please explain, how does Java implements such future task.

2

There are 2 answers

0
ΦXocę 웃 Пepeúpa ツ On

My expectation was get() might return null...

no, get will return only the possible values returned by the call method in the TThread class, in your case "Something", the option left is that the call method gets interrupted, then there is no return at all since the exception propagates in the stack up to the
executor.submit() invocation.

Your micro-benchmark in Java is not working as it should...

0
Tatarize On

"So JIT, would not be aware of that developer called the get() or not in upcoming codes. So my point is; whenever submit(callable) is being called, does JVM preserves the result irrespective of get() is being called or not?"

It occurs to me that your understanding here is a big problem. First off neither the JIT nor JVM cares about such high level commands. They are the result of the ExecutorService class which implements it. When you call it with a particular result providing thread it returns an object called a future task. The task itself is just an empty shell that basically returns the value if it has the value and if it doesn't it calls wait(). This causes the thread halt operation. Then the other thread that the ExecutorService called up returns its value. And when it returns the value it passes that value into the FutureTask in such a way that it calls notify() which starts the thread back up.

The ExecutorService class is doing the work. You passed it a Callable which it then kicked off and called. And returned a token class that provides the future location of the result. This class simply returns the value it has, or forces the threat that calls .get() to halt operation until it can return that value.

You can implement the same logic yourself. The JVM is concerned about the thread stuff the wait() and notify() commands as such but not the higher level stuff like the fancy wrapper class to setup the way the paradigm works here. There's a number of other such things like BlockingQueues and AsyncTasks and a bunch of other helper classes that do the internal logic of running things in parallel. Get has logic that basically says: If I have the value, return the value. If I don't wait. Then return the value. And the trick is the other thread you kicked up during the submit has code that said put the value in that class and notify() any stopped threads waiting on that object.

The JVM doesn't deal with this class, just the execution of the commands in more or less bytecode, the JIT compiles it a bit more while it's running. But, it's not running java source code, it doesn't know what this class is by that time. But, it does know how to to make the thread wait until there's a value because that's what the Executor code actually does, it just does some of the helpful work for you because dealing with the blocking yourself is a harrowing task.

It's not actually predicting the future. It's the place where the future result is to be placed. And if you call it early, your thread is paused until the result is available.


You benchmark the wrong thing. But, no. .get() will "Waits if necessary for the computation to complete, and then retrieves its result." so. What happens is expected. It's just the result is kept in the future task. No matter how long you sleep the main thread your setup will always have the result given to you. It's just that you hold off calling the .get() until the last moment so you can run the operations in parallel.

public class Main {
    public static void main(String[] args) throws InterruptedException, ExecutionException {        
        ExecutorService exec =  Executors.newFixedThreadPool(1);
        Future<String> f =  exec.submit(new TThread());
        Thread.sleep(10000);
        System.out.println("Main Thread "+System.currentTimeMillis());      
        String s  = (String) f.get();
        System.out.println(s);
        exec.shutdown();
    }
}

You submitted the thread. Slept for 10 second. Then reported the time. 10 seconds after the start of the thread. You then called .get() which waits if needed but the TThread is already done so it returned the answer.

class TThread implements Callable<String>{
    @Override
    public String call() throws Exception{
        Thread.sleep(3000);
        System.out.println("TThread Thread "+System.currentTimeMillis());
        return "something";
    }
}

You slept 3 seconds and output the time 3 seconds after the program begins execution. And returned something.

This is exactly your results. The point is if you need the answer to the thread you call get, and it will get the answer. But, if you can do other things for a while, and then call get, you ran in parallel for that time and whether you have the answer or not, you get the result. But, it causes the current thread to block if it's not done yet. In this case the result had the answer and just returned it to you. But, if you flip the times. You will still get the same stuff. The main thread would report 3 seconds and the TThread will report 10. What you need to do is report the time after the get command. And in that case you will see 10 for the main and 3 for the other. But, when you switch the times you'll get 3 for the main, 7 for the get, 10 for the other thread.