When using thread pool, I found that in addition to the execute() method that can execute tasks, I also found that there is a method that submit() can execute tasks.
submit() has 3 methods with different parameters. These methods are declared in the ExecutorService interface and implemented in the AbstractExecutorService, while ThreadPoolExecutor inherits the AbstractExecutorService.
<T> Future<T> submit(Callable<T> callable); <T> Future<T> submit(Runnable var1, T result); Future<?> submit(Runnable runnable);
We can see that the parameter of submit() can be either Runnable or Callable. We are quite familiar with Runnable. It is a task executed by the thread Thread. There is a run() method inside, which is the specific execution operation of the task. So what about Callable? Let's take a look at their code together.
public interface Runnable { void run(); } public interface Callable<V> { V call() throws Exception; }
Runnable will not be introduced here. The Callable interface defines a call() method, which returns a generic class specified by Callable, and an exception will be thrown when calling call(). By comparing Runnable and Callable, there is no clue, so let’s take a look at the internal implementation.
Submit() parameter analysis
Here we focus on analyzing the method of submit() with parameters Runnable and Callable
public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFuture<Void> ftask = newTaskFor(task, null); execute(ftask); return ftask; } public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); execute(ftask); return ftask; }
We found that there is no difference in the implementation of the two, the only thing is that the submit() parameter is different.
Pass the parameter into the newTaskFor() method, then it is certain that what operations were done in this method.
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { return new FutureTask<T>(runnable, value); } protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { return new FutureTask<T>(callable); }
The purpose of newTaskFor() is to create a FutureTask object, so we trace the FutureTask construction method (FutureTask is very critical, and will be analyzed later).
public FutureTask(Runnable runnable, V result) { = (runnable, result); = NEW; } public FutureTask(Callable<V> callable) { if (callable == null)throw new NullPointerException(); = callable; = NEW; }
When we come here, we know that Runnable will actually be converted into Callable here. Let’s take a look at the specific implementation of ().
public static <T> Callable<T> callable(Runnable task, T result) { if (task == null) throw new NullPointerException(); return new RunnableAdapter<T>(task, result); } private static final class RunnableAdapter<T> implements Callable<T> { private final Runnable task; private final T result; RunnableAdapter(Runnable task, T result) { = task; = result; } public T call() { (); return result; } }
() creates a RunnableAdapter object. RunnableAdapter implements the Callable interface. The incoming Runnable run() is called in the call() method, and the incoming result parameter is returned.
That is to say, the Runnbale we call submit() will eventually be converted into Callable and return a result value (if we pass in this parameter, it will return this parameter, and if we don't pass in, it will return null).
At this point, we have explained the differences and internal implementation of the parameters of submit(). The submit() method has a return value Future. Let's analyze the return value Future.
Return value of submit() Future
As we analyze the submit() source code above, we can see that submit() returns a RunnableFuture class object, which is really a new FutureTask() object through the newTaskFor() method. So the real object returned by submit() is the FutureTask object.
So what is FutureTask? Let’s take a look at its class inheritance relationship.
public class FutureTask<V> implements RunnableFuture<V> { ... } public interface RunnableFuture<V> extends Runnable, Future<V> { void run(); }
Through the inheritance relationship, we can clearly know that FutureTask is actually a Runnable. And it has its own run() implementation. Let's take a look at how FutureTask's run() is implemented.
public void run() { if (state != NEW || !(this, RUNNER, null, ())) return; try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { result = (); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { // runner must be non-null until state is settled to // prevent concurrent calls to run() runner = null; // state must be re-read after nulling runner to prevent // leaked interrupts int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }
When we use the new FutureTask() object, the state state will be assigned to NEW in the FutureTask constructor and a callable object will be passed. Through the run() of FutureTask, we can know that in fact, we can judge the state state and call the callable call(). (If the passed parameter is Runnable, when Runnable is converted into the RunnableAdapter class, in call(), the run() method of Runnable is actually called).
So in the submit() method, an execute(task) method is called, and the actual execution of FutureTask's run(), and FutureTask's run() calls Callable's call() method.
Having said so much, the last thing that submit() executes is the run() or call() method of the call() passed in Runnable. It seems that there is no FutureTask.
Actually, it is not. submit() returns the FutureTask object. Calling get() through this FutureTask object can return a generic class parameter result object passed into the submit() method. If it is Callable, it will be returned directly through call(). This return value can be used to verify whether the task execution is successful.
The implementation of FutureTask's get()
public V get() throws InterruptedException, ExecutionException { int s = state; if (s <= COMPLETING) s = awaitDone(false, 0L); //Waiting for the task to be completed return report(s);//Return the executed task result} private V report(int s) throws ExecutionException { Object x = outcome; if (s == NORMAL) return (V)x; if (s >= CANCELLED) throw new CancellationException(); throw new ExecutionException((Throwable)x); }
Finally, the result will be returned according to the status of the task through the outcome parameter. So where is the outcome parameter assigned? There are two better places for assignment of the outcome parameter: one is the set() of FutureTask, and the other is the setException() of FutureTask.
set() is to assign the passed result parameter to set() after the execution of the FutureTask run() is completed, and the result parameter is assigned to the outcome parameter. If run() reports an exception, the Throwable object will be passed in through the setException() method and assigned to the outcome variable
You can return to the run() above to view it.
protected void set(V v) { if ((this, STATE, NEW, COMPLETING)) { outcome = v; (this, STATE, NORMAL); // final state finishCompletion(); } } protected void setException(Throwable t) { if ((this, STATE, NEW, COMPLETING)) { outcome = t; (this, STATE, EXCEPTIONAL); // final state finishCompletion(); } }
Submit() use case
public class Test { private static final String SUCCESS = "success"; public static void main(String[] args) { ExecutorService executorService = (3); ("------------------------------------------------------------------------------------------------------------------------------); Future<String> future = (new Callable<String>() { @Override public String call() throws Exception { (5000); ("Submit method execution task completed" + " thread name: " + ().getName()); return SUCCESS; } }); try { String s = (); if ((s)) { String name = ().getName(); ("After the return value comparison, the submit method successfully executes the task thread name: " + name); } } catch (InterruptedException e) { (); } catch (ExecutionException e) { (); } ("-------------------main thread end---------------------"); } }
Print result:
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Call() call starts: 1496899867882
The execution task of submit method is completed: 1496899872897 thread name: pool-1-thread-1
After comparison of return value, the submit method successfully executes the task thread name: main
-------------------main thread end---------------------
The main thread will keep blocking and wait for the task in the thread pool to be executed and then execute the following statements.
This is the article about the detailed explanation of the submit() method in ThreadPoolExecutor. For more related content of ThreadPoolExecutor submit() method, please search for my previous article or continue browsing the related articles below. I hope everyone will support me in the future!