My question is about InterruptedException
, which is thrown from the Thread.sleep
method. While working with ExecutorService
I noticed some weird behaviour that I don't understand; here is what I mean:
ExecutorService executor = Executors.newSingleThreadExecutor(); executor.submit(() -> { while(true) { //DO SOMETHING Thread.sleep(5000); } });
With this code, the compiler doesn't give me any error or message that InterruptedException
from Thread.sleep
should be caught. But when I am trying to change the loop condition and replace "true" with some variable like this:
ExecutorService executor = Executors.newSingleThreadExecutor(); executor.submit(() -> { while(tasksObserving) { //DO SOMETHING Thread.sleep(5000); } });
The compiler constantly complains that InterruptedException
has to be handled. Can someone explain to me why this happens, and why if the condition is set to true the compiler ignores the InterruptedException?
the Thread.sleep () method can throw an InterruptedException, and I want to be sure I know what this exception implies and therefore how to handle it. My understanding is that this exception if thrown, could mean that the sleep () has terminated before the specified duration has completed. Is this correct?
Thread Class is a class that is basically a thread of execution of the programs. It is present in Java.lang package. Thread class contains the Sleep () method. There are two overloaded methods of Sleep () method present in Thread Class, one is with one argument and another one is with two arguments.
There are several methods in Java that throw InterruptedException. These include Thread.sleep (), Thread.join (), the wait () method of the Object class, and put () and take () methods of BlockingQueue, to name a few. 3.3. Interruption Methods in Threads
Thread provides the interrupt () method for interrupting a thread, and to query whether a thread has been interrupted, we can use the isInterrupted () method. Occasionally, we may wish to test whether the current thread has been interrupted and if so, to immediately throw this exception.
The reason for this, is that these invocations are in fact, invocations to two different overloaded methods available in ExecutorService
; each of these methods taking a single argument of different types:
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
Then what happens is that the compiler is converting the lambda in the first case of your problem into a Callable<?>
functional interface (invoking the first overloaded method); and in the second case of your problem converts the lambda into a Runnable
functional interface (invoking therefore the second overloaded method), requiring because of this to handle the Exception
thrown; but not in the previous case using the Callable
.
Although both functional interfaces don't take any arguments, Callable<?>
returns a value:
- Callable:
V call() throws Exception;
- Runnable:
public abstract void run();
If we switch to examples that trim the code to the relevant pieces (to easily investigate just the curious bits) then we can write, equivalently to the original examples:
ExecutorService executor = Executors.newSingleThreadExecutor(); // LAMBDA COMPILED INTO A 'Callable<?>' executor.submit(() -> { while (true) throw new Exception(); }); // LAMBDA COMPILED INTO A 'Runnable': EXCEPTIONS MUST BE HANDLED BY LAMBDA ITSELF! executor.submit(() -> { boolean value = true; while (value) throw new Exception(); });
With these examples, it may be easier to observe that the reason why the first one is converted to a Callable<?>
, while the second one is converted to a Runnable
is because of compiler inferences.
In both cases, the lambda bodies are void-compatible, since every return statement in the block has the form return;
.
Now, in the first case, the compiler does the following:
throw new <CHECKED_EXCEPTION>()
.complete normally
and therefore is value-compatible.Callable<?>
and Runnable
are potential matches for this lambda, the compiler selects the most specific match (to cover all scenarios); which is the Callable<?>
, converting the lambda into an instance of it and creating an invocation reference to the submit(Callable<?>)
overloaded method.While, in the second case, the compiler does the following:
complete normally
.Runnable
(as it is the only available fitting functional interface for the lambda to be converted into) and creates an invocation reference to the submit(Runnable)
overloaded method. All this coming at the price of delegating to the user, the responsibility of handling any Exception
s thrown wherever they MAY occur within portions of the lambda body.This was a great question - I had a lot of fun chasing it down, thanks!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With