Lambda中无限while循环内的Thread.sleep不需要'catch(InterruptedException)'-为什么不呢? [英] Thread.sleep inside infinite while loop in lambda doesn't require 'catch (InterruptedException)' - why not?
问题描述
我的问题是关于InterruptedException
,它是从Thread.sleep
方法抛出的.在使用ExecutorService
时,我注意到一些我不理解的怪异行为.这就是我的意思:
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);
}
});
使用此代码,编译器不会向我发出任何错误或消息,提示应该捕获Thread.sleep
中的InterruptedException
.但是,当我尝试更改循环条件并用诸如此类的变量替换"true"时:
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);
}
});
编译器经常抱怨必须处理InterruptedException
.有人可以向我解释为什么会发生这种情况,为什么将条件设置为true则编译器会忽略InterruptedException?
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?
推荐答案
之所以这样做,是因为这些调用实际上是对ExecutorService
中提供的两种不同的重载方法的调用;这些方法中的每一个都采用不同类型的单个参数:
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);
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
然后发生的事情是,编译器将问题的第一种情况下的lambda转换为Callable<?>
功能接口(调用第一个重载方法);在您遇到问题的第二种情况下,将lambda转换为Runnable
功能接口(因此调用第二个重载方法),因此需要处理抛出的Exception
;但在以前的情况下则不使用Callable
.
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
.
尽管两个功能接口均不接受任何参数,但Callable<?>
返回值:
Although both functional interfaces don't take any arguments, Callable<?>
returns a value:
- 可致电:
V call() throws Exception;
- 可运行:
public abstract void run();
- 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();
});
在这些示例中,可能更容易观察到第一个被转换为Callable<?>
而第二个被转换为Runnable
的原因是由于编译器推断.
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.
在两种情况下,lambda主体为无效兼容,因为该块中的每个return语句的格式均为return;
.
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:
- 检测到lambda中的所有执行路径都声明抛出已检查的异常(从现在开始,我们将其称为'exception',仅表示'checked exceptions').这包括调用任何声明抛出异常的方法,以及对
throw new <CHECKED_EXCEPTION>()
的显式调用. - 正确推断lambda的 WHOLE 主体等效于声明抛出异常的代码块;当然,必须必须:处理或重新抛出.
- 由于lambda不处理异常,因此编译器默认情况下假定必须重新抛出这些异常.
- 安全地推断此lambda必须与功能接口匹配,不能
complete normally
,因此为
- 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>()
. - 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 re-thrown.
- Since the lambda is not handling the exception, then the compiler defaults to assume that these exception(s) must be re-thrown.
- Safely infers that this lambda must match a functional interface cannot
complete normally
and therefore is value-compatible. - Since
Callable<?>
andRunnable
are potential matches for this lambda, the compiler selects the most specific match (to cover all scenarios); which is theCallable<?>
, converting the lambda into an instance of it and creating an invocation reference to thesubmit(Callable<?>)
overloaded method.
在第二种情况下,编译器执行以下操作:
While, in the second case, the compiler does the following:
- 检测到lambda中可能存在请勿声明抛出异常的执行路径(取决于要评估的逻辑).
- 由于并非所有执行路径都声明抛出异常,因此编译器得出结论,lambda的主体与声明抛出异常的代码块 NOT NECESSARILY 等效-编译器不在乎/注意是否该代码的某些部分确实声明了它们可以(无论整个主体是否这样做).
- 安全地推断出lambda不是
- Detects that there may be execution paths in the lambda that DO NOT declare throwing exceptions (depending on to-be-evaluated logic).
- 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.
- Safely infers that the lambda is not value-compatible; since it MAY
complete normally
. - Selects
Runnable
(as it is the only available fitting functional interface for the lambda to be converted into) and creates an invocation reference to thesubmit(Runnable)
overloaded method. All this coming at the price of delegating to the user, the responsibility of handling anyException
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!
这篇关于Lambda中无限while循环内的Thread.sleep不需要'catch(InterruptedException)'-为什么不呢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!