java - lambda 中的无限 while 循环内的 Thread.sleep 不需要 'catch (InterruptedException)' - 为什么不呢?

标签 java multithreading lambda compilation functional-interface

我的问题是关于 InterruptedException,它是从 Thread.sleep 方法抛出的。在使用 ExecutorService 时,我注意到一些我不理解的奇怪行为;这就是我的意思:

ExecutorService executor = Executors.newSingleThreadExecutor();
    executor.submit(() -> {
        while(true)
        {
            //DO SOMETHING
            Thread.sleep(5000);
        }
    });

使用此代码,编译器不会向我提供任何应捕获来自 Thread.sleepInterruptedException 的错误或消息。但是当我尝试更改循环条件并用这样的变量替换“true”时:

ExecutorService executor = Executors.newSingleThreadExecutor();
    executor.submit(() -> {
        while(tasksObserving)
        {
            //DO SOMETHING
            Thread.sleep(5000);
        }
    });

编译器不断提示必须处理InterruptedException。有人可以向我解释为什么会发生这种情况,以及为什么如果条件设置为 true 编译器会忽略 InterruptedException?

最佳答案

这样做的原因是,这些调用实际上是对 ExecutorService 中可用的两个不同重载方法的调用。 ;这些方法中的每一个都采用不同类型的单个参数:

  1. <T> Future<T> submit(Callable<T> task); 2. Future<?> submit(Runnable task);

然后发生的情况是,编译器将问题第一种情况下的 lambda 转换为 Callable<?>函数式接口(interface)(调用第一个重载方法);在问题的第二种情况下,将 lambda 转换为 Runnable功能接口(interface)(因此调用第二个重载方法),因此需要处理 Exception抛出;但在之前使用 Callable 的情况下则不然.

虽然两个函数式接口(interface)都不接受任何参数,Callable<?> 返回一个值:

  1. 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; 的形式.

现在,在第一种情况下,编译器执行以下操作:

  1. 检测到 lambda 中的所有执行路径都声明抛出 checked exceptions (从现在开始,我们将称为“异常”,仅表示“已检查异常”)。这包括调用任何声明抛出异常的方法以及对 throw new <CHECKED_EXCEPTION>() 的显式调用.
  2. 正确得出 lambda 的整个主体相当于声明抛出异常的代码块;当然必须是:处理或重新抛出。
  3. 由于 lambda 不处理异常,因此编译器默认假设必须重新引发这些异常。
  4. 安全地推断此 lambda 必须与功能接口(interface)匹配,不能complete normally因此是 value-compatible .
  5. Callable<?>Runnable是此 lambda 的潜在匹配项,编译器会选择最具体的匹配项(以涵盖所有场景);这是 Callable<?> ,将 lambda 转换为其实例并创建对 submit(Callable<?>) 的调用引用重载方法。

在第二种情况下,编译器执行以下操作:

  1. 检测到 lambda 中可能存在声明抛出异常的执行路径(取决于要评估的逻辑)。
  2. 由于并非所有执行路径都声明抛出异常,因此编译器得出结论,lambda 的主体不一定相当于声明抛出异常的代码块 - 编译器不关心/注意以下情况:代码的某些部分确实声明它们可以,只要整个代码体声明或不声明即可。
  3. 安全地推断 lambda 不是 value-compatible ;因为它五月complete normally .
  4. 选择Runnable (因为它是要转换为 lambda 的唯一可用拟合功能接口(interface))并创建对 submit(Runnable) 的调用引用重载方法。所有这一切都是以将处理任何Exception的责任委托(delegate)给用户为代价的。任何它们可能出现在 lambda 主体部分内的地方都会被抛出。

这是一个很好的问题 - 我在追寻它的过程中获得了很多乐趣,谢谢!

关于java - lambda 中的无限 while 循环内的 Thread.sleep 不需要 'catch (InterruptedException)' - 为什么不呢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58611000/

相关文章:

c++ - 包装右值引用 lambda 时 std::async 和 std::bind 之间的区别

java - install4j 生成的可执行文件不启动

java - 在 while 循环中等待(长时间超时)?

java - 多线程写入和多线程读取的 ConcurrentLinkedQueue 的并发问题。 #快速目录扫描

python - 使用 Lambda 创建自定义 Keras Layer 对象

linq - 如何在 lambda 表达式中调用方法?

java - 使用 playframework 在 java 中实现 WURFL Cloud API

java - Blackberry Java 可以做参数化类型吗?

如果使用 nu.pattern.OpenCV.loadShared() 加载 openCV,Java open CV 会卡在 VideoCapture 上以获取文件;

c - 从另一个线程访问主线程的局部变量