Quiz yourself: The overloaded submit(…) methods in Java’s ExecutorService

August 21, 2023 | 4 minute read
Text Size 100%:

Know when to use Runnable and Callable in multithreaded code.

More quiz questions available here

Given the following code fragment

Copied to Clipboard
Error: Could not Copy
Copied to Clipboard
Error: Could not Copy
00:  ExecutorService es = ...
01:  // es.submit(() -> {;} );
02:  // es.submit(() -> null );
03:  // es.submit(() -> { throw new NullPointerException(); });
04:  // es.submit(() -> { throw new IOException(); });
05:  // es.submit(() ->  new SQLException());

Which line or lines, when uncommented individually, will compile successfully? Choose one.

A. Only line 01
B. Only lines 01 and 02
C. Only lines 01, 02, and 03
D. Only lines 01, 02, 03, and 04
E. All lines will compile successfully

Answer. The java.util.concurrent.ExecutorService has three overloaded submit(...) methods.

  • <T> Future<T> submit(Callable<T> task);
  • Future<?> submit(Runnable task);
  • <T> Future<T> submit(Runnable task, T result);

Each of these methods takes an object that defines a task and returns an object that allows you to interact with that task and, in particular, obtain a result from it after it is completed. In each case, the task is defined by a particular method on the argument object, and that task-defining method is declared in the interface Callable or Runnable, depending on the submit method invoked. Those interfaces have the following forms:

Copied to Clipboard
Error: Could not Copy
Copied to Clipboard
Error: Could not Copy
public interface Runnable {
    public abstract void run();
}

public interface Callable<V> {
    V call() throws Exception;
}

Notice that there are two significant differences between them.

  • A Callable returns a value, whereas the Runnable declares a void method.
  • A Callable may throw a checked exception, but a Runnable can throw only an unchecked exception.

Consider the ExecutorService methods listed above in light of this. In the first overloaded submit(…) method, the Future will give access—after the task is completed—to the value of type T that is returned by the Callable or, if the method threw a checked exception, to that exception. This access happens using the get() method of the Future. If the task was completed normally, the get() method typically returns the value returned by the task. If the task threw an exception, the get() method throws an ExecutionException, the cause of which is the exception thrown by the task.

Did you notice the vague wording “the get() method typically returns…” in the description above? The second and third overloaded submit(…) methods both take a Runnable, so no value can be provided by the task. In the case of the second method, the Future returns null if the task is completed normally. By contrast, the Future returned by the third method will return the value passed as the second argument (named result in the signature shown) when Runnable is completed normally.

It’s time to see how the compiler will view each of the proposed tasks—defined by lambda expressions—in the quiz question.

Line 01: () -> {;}

This lambda body does not return any value, so it can implement only Runnable. The method has an empty body and correctly forms a void method. It’s perhaps a little surprising to see the semicolon standing by itself, and certainly that is redundant, but Java allows semicolons to be scattered in source code anywhere that a statement is expected. From this you can see that line 01 is valid and will compile.

Line 02: () -> null

This form creates a Callable because it returns a value. A Runnable must not return anything at all; null is a value and is not compatible with a void return. The essential detail here is that the code compiles; therefore, line 02 is also valid.

In lines 03 and 04, the lambda has a body that consistently throws an exception. A Runnable can throw only unchecked exceptions, but a Callable may throw any exception. This means that line 03 could be either a Runnable or a Callable, but line 04 must be a Callable. Again, however, the essence is that both lines are valid and will compile.

Line 05: () -> new SQLException()

This one is a little surprising in that it returns an exception, rather than throwing an exception. However, exceptions are objects, so the code forms a Callable because it returns a value. Therefore line 05 is valid and will compile.

Since all five lambdas are valid, the correct answer is option E.

Conclusion. The correct answer is option E.

Related quizzes

Simon Roberts

Simon Roberts joined Sun Microsystems in time to teach Sun’s first Java classes in the UK. He created the Sun Certified Java Programmer and Sun Certified Java Developer exams. He wrote several Java certification guides and is currently a freelance educator who publishes recorded and live video training through Pearson InformIT (available direct and through the O’Reilly Safari Books Online service). He remains involved with Oracle’s Java certification projects.

Mikalai Zaikin

Mikalai Zaikin is a lead Java developer at IBA Lithuania (part of worldwide IBA Group) and currently located in Vilnius. During his career, Zaikin has helped Oracle with development of Java certification exams, and he has been a technical reviewer of several Java certification books, including three editions of the famous Sun Certified Programmer for Java study guides by Kathy Sierra and Bert Bates.


Previous Post

Java 21 sneak peek

Mohamed Taman | 11 min read

Next Post


Using Helidon and MySQL Document Store to work with diverse databases

Dmitry Aleksandrov | 6 min read
Oracle Chatbot
Disconnected