我的问题是关于 InterruptedException
,它是从 Thread.sleep
方法抛出的。在使用 ExecutorService 时,我注意到一些我不理解的奇怪行为;这就是我的意思:
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
while(true)
{
//DO SOMETHING
Thread.sleep(5000);
}
});
使用此代码,编译器不会向我提供任何应捕获来自 Thread.sleep
的 InterruptedException
的错误或消息。但是当我尝试更改循环条件并用这样的变量替换“true”时:
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
while(tasksObserving)
{
//DO SOMETHING
Thread.sleep(5000);
}
});
编译器不断提示必须处理InterruptedException
。有人可以向我解释为什么会发生这种情况,以及为什么如果条件设置为 true 编译器会忽略 InterruptedException?
最佳答案
这样做的原因是,这些调用实际上是对 ExecutorService
中可用的两个不同重载方法的调用。 ;这些方法中的每一个都采用不同类型的单个参数:
<T> Future<T> submit(Callable<T> task);
2.Future<?> submit(Runnable task);
然后发生的情况是,编译器将问题第一种情况下的 lambda 转换为 Callable<?>
函数式接口(interface)(调用第一个重载方法);在问题的第二种情况下,将 lambda 转换为 Runnable
功能接口(interface)(因此调用第二个重载方法),因此需要处理 Exception
抛出;但在之前使用 Callable
的情况下则不然.
虽然两个函数式接口(interface)都不接受任何参数,Callable<?>
返回一个值:
- Callable<?>:
V call() throws Exception;
2. Runnable:public abstract void run();
如果我们切换到将代码修剪为相关部分的示例(以便轻松研究好奇的部分),那么我们可以编写,相当于原始示例:
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
是因为编译器推断。
在这两种情况下,lambda 体都是 void-compatible ,因为 block 中的每个 return 语句都具有 return;
的形式.
现在,在第一种情况下,编译器执行以下操作:
- 检测到 lambda 中的所有执行路径都声明抛出 checked exceptions (从现在开始,我们将称为“异常”,仅表示“已检查异常”)。这包括调用任何声明抛出异常的方法以及对
throw new <CHECKED_EXCEPTION>()
的显式调用. - 正确得出 lambda 的整个主体相当于声明抛出异常的代码块;当然必须是:处理或重新抛出。
- 由于 lambda 不处理异常,因此编译器默认假设必须重新引发这些异常。
- 安全地推断此 lambda 必须与功能接口(interface)匹配,不能
complete normally
因此是 value-compatible . - 自
Callable<?>
和Runnable
是此 lambda 的潜在匹配项,编译器会选择最具体的匹配项(以涵盖所有场景);这是Callable<?>
,将 lambda 转换为其实例并创建对submit(Callable<?>)
的调用引用重载方法。
在第二种情况下,编译器执行以下操作:
- 检测到 lambda 中可能存在不声明抛出异常的执行路径(取决于要评估的逻辑)。
- 由于并非所有执行路径都声明抛出异常,因此编译器得出结论,lambda 的主体不一定相当于声明抛出异常的代码块 - 编译器不关心/注意以下情况:代码的某些部分确实声明它们可以,只要整个代码体声明或不声明即可。
- 安全地推断 lambda 不是 value-compatible ;因为它五月
complete normally
. - 选择
Runnable
(因为它是要转换为 lambda 的唯一可用拟合功能接口(interface))并创建对submit(Runnable)
的调用引用重载方法。所有这一切都是以将处理任何Exception
的责任委托(delegate)给用户为代价的。任何它们可能出现在 lambda 主体部分内的地方都会被抛出。
这是一个很好的问题 - 我在追寻它的过程中获得了很多乐趣,谢谢!
关于java - lambda 中的无限 while 循环内的 Thread.sleep 不需要 'catch (InterruptedException)' - 为什么不呢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58611000/