Friday, August 25, 2023

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

Quiz Yourself, Java’s ExecutorService, Oracle Java Career, Java Skills, Java Jobs, Java Prep, Java Preparation, Java Tutorial and Materials

Know when to use Runnable and Callable in multithreaded code.


Given the following code fragment

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:

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.

Source: oracle.com

Related Posts

0 comments:

Post a Comment