By schemaboi


2019-05-15 12:48:48 8 Comments

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?

1 comments

@Marco R. 2019-05-15 13:20:04

The reason for this is that these invocations are, in fact, invocations to two different overloaded methods, taking two different type of arguments, each type with different exception handling specifications:

  1. <T> Future<T> submit(Callable<T> task);
  2. 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).

Although both functional interfaces don't take arguments, Callable<?> returns a value and it throws Exception (very important!):

  1. Callable: V call() throws Exception;
  2. 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 the first case, the compiler does the following:

  1. Detects that all execution paths in the lambda declare throwing checked exceptions (from now on we will refer as 'exception', implying only 'checked exceptions'). This includes the invocation of any method declaring throwing exceptions and the explicit invocation to throw new <CHECKED_EXCEPTION>().
  2. Concludes correctly that the WHOLE body of the lambda is equivalent to a block of code declaring throwing exceptions; which of course MUST be either: handled or rethrown.
  3. Since the lambda is not handling the exception, then the compiler defaults to assume that these exception(s) must be rethrown.
  4. Safely infers that this lambda must match a functional interface that throws Exception.
  5. Since Callable<?> is the only matching functional interface available for the overloaded methods available, it selects it, converts the lambda into a Callable<?> and creates an invocation reference to the submit(Callable<?>) overloaded method.

In the second case, the compiler does the following:

  1. Detects that there may be execution paths in the lambda that DO NOT declare throwing exceptions (depending on to-be-evaluated logic).
  2. Since not all execution paths declare throwing exceptions, the compiler concludes that the body of the lambda is NOT NECESSARILY equivalent to a block of code declaring throwing exceptions - compiler doesn't care/pay attention if some portions of the code do declare that they may, only if the whole body does or not.
  3. The compiler dismisses Callable<?> as a matching functional interface for the lambda, since Callable does declare throwing exceptions. (a)
  4. Selects Runnable as the remaining 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 Exceptions thrown wherever they MAY occur within portions of the lambda body.

(a) The compiler has no impeding reason not to default to convert always all lambdas to Callable<?> (and ease inner complications), other than having Callable<?> be a more restrictive functional interface than available, viable, alternatives (i.e. Runnable). This behavior is in line with the principle of 'being as restrictive as one MUST; but as unrestrictive as one CAN'.

This was a great question - I had a lot of fun chasing it down, thanks!

@VGR 2019-05-15 14:07:22

I think you have reversed Runnable and Callable. The first code block does not require catching of InterruptedException because the lambda is interpreted as a Callable. Also, I do not understand your explanation of why one lambda is interpreted as a Callable but not the other one; both are equally likely to throw InterruptedException.

@Marco R. 2019-05-15 14:22:55

You are right, I accidentally swapped them relative to the question when working them out the more focus examples. Regarding interpretation to Callable, it is a compiler optimization, I'll elaborate in the answer.

@gaurav 2019-05-15 15:22:09

@MarcoR. flawless answer! Kudos!

@Ertai87 2019-05-15 19:55:14

@MarcoR. This answer is great and informative! One question though (as a reader of this question purely for knowledge's sake): Why can we safely and without loss of generality convert Thread.sleep (in the OP) to throw new Exception (in your answer)? As far as I'm aware, Thread.sleep does not always throw an exception, and thus it occurs to me that in either case the compiler should infer that both cases may or may not throw an exception based on system state at runtime. What am I missing?

@Marco R. 2019-05-15 21:23:43

That's a good question, it was just poor choice of words on my part; it is not as much as the body of the lambda may/will throw exception(s) as it is that the body of the lambda is equivalent to declaring throwing exception(s) (which must be catch or rethrown). I edited the answer to be more precise around this potential confusion. Thanks for the heads up, lmk if it still seems unclear or if you have any other question.

@Ertai87 2019-05-16 15:50:32

@MarcoR. I'm still somewhat unclear. Would it be correct to say the following? Since in the first case, in the original post, we have a while true (which we always enter), and the body of the while true throws an exception (in the sense that it consists of one or more method calls whose method signatures have throws clauses, not in the sense that they always throw an exception in all cases), then the compiler infers that the matching overloaded interface for the entire lambda must also throw an exception; in the latter case because the loop is a while condition, then it is not guaranteed...

@Ertai87 2019-05-16 15:52:18

... that every control flow of that lambda contains any method call whose method signature contains a throws clause, and therefore it fits to the overloaded interface which does not throw an exception by default. This would be kind of a very strange and complex compiler optimization, I suppose, which is why I find it somewhat hard to believe, although it would certainly not be the strangest thing I've heard of Java doing.

@Marco R. 2019-05-16 17:03:16

@Ertai87 The compiler doesn't infer runtime behavior, so it's not as much as inferring what the code is going to do, as much as what it declares of potentially doing. In the Callable case, the lambda body can be correctly interpreted of declaring as a whole capable of throwing exceptions (which must be handled of declare rethrowing). In the Runnable case, the lambda body CANNOT be generalized as declaring as a whole of throwing exceptions - compiler doesn't care/pay attention if some portions of the code do declare that they may, only if the whole body does or not.

@Ertai87 2019-05-16 17:26:19

Neat! Thanks, very informative!

Related Questions

Sponsored Content

2 Answered Questions

[SOLVED] Mockito test a void method throws an exception

24 Answered Questions

[SOLVED] Java 8 Lambda function that throws exception?

  • 2013-08-12 23:16:36
  • Triton Man
  • 253744 View
  • 404 Score
  • 24 Answer
  • Tags:   java lambda java-8

32 Answered Questions

[SOLVED] Why doesn't RecyclerView have onItemClickListener()?

13 Answered Questions

[SOLVED] Missing return statement in a non-void method compiles

  • 2013-05-28 10:32:48
  • c.P.u1
  • 8655 View
  • 189 Score
  • 13 Answer
  • Tags:   java c# oop

22 Answered Questions

[SOLVED] Why doesn't Java allow overriding of static methods?

26 Answered Questions

[SOLVED] Why does this go into an infinite loop?

10 Answered Questions

[SOLVED] Why doesn't JUnit provide assertNotEquals methods?

  • 2009-07-08 07:46:43
  • Chris B
  • 156764 View
  • 413 Score
  • 10 Answer
  • Tags:   java junit assert

13 Answered Questions

[SOLVED] I get exception when using Thread.sleep(x) or wait()

  • 2010-07-27 10:28:27
  • vincent low
  • 882076 View
  • 339 Score
  • 13 Answer
  • Tags:   java sleep

3 Answered Questions

[SOLVED] Thread.sleep() in a while loop

  • 2012-01-09 17:46:54
  • Steve Ferguson
  • 27314 View
  • 21 Score
  • 3 Answer
  • Tags:   java while-loop sleep

Sponsored Content