Java Thread.sleep在无限范围内,而lambda中的循环不';不需要';捕获(中断异常)和#x27;-为什么不呢?

Java Thread.sleep在无限范围内,而lambda中的循环不';不需要';捕获(中断异常)和#x27;-为什么不呢?,java,multithreading,lambda,compilation,functional-interface,Java,Multithreading,Lambda,Compilation,Functional Interface,我的问题是关于InterruptedException,它是从线程.sleep方法抛出的。在使用ExecutorService时,我注意到一些我不理解的奇怪行为;我的意思是: ExecutorService executor = Executors.newSingleThreadExecutor(); executor.submit(() -> { while(true) { //DO SOMETHING

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

ExecutorService executor = Executors.newSingleThreadExecutor();
    executor.submit(() -> {
        while(true)
        {
            //DO SOMETHING
            Thread.sleep(5000);
        }
    });
使用这段代码,编译器不会给我任何错误或消息,说明
InterruptedException
来自
Thread.sleep
应该被捕获。但当我试图更改循环条件并用如下变量替换“true”时:

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

编译器不断抱怨必须处理
InterruptedException
。有人能给我解释一下为什么会发生这种情况,以及为什么如果条件设置为true,编译器会忽略InterruptedException吗?

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

  • 未来提交(可调用任务)
  • 未来提交(可运行任务)
  • 然后发生的情况是,编译器正在将问题的第一种情况下的lambda转换为可调用的函数接口(调用第一个重载方法);在问题的第二种情况下,将lambda转换为
    可运行的
    函数接口(因此调用第二个重载方法),因此需要处理抛出的
    异常
    ;但在前一种情况下,不能使用
    可调用的

    尽管两个函数接口都不接受任何参数,
    Callable
    返回一个值:

  • 可调用:
    V call()引发异常
  • 可运行:
    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();
        });
    
    ExecutorService executor=Executors.newSingleThreadExecutor();
    //LAMBDA编译为“可调用”
    执行人提交(()->{
    while(true)
    抛出新异常();
    });
    //LAMBDA编译为“可运行”:异常必须由LAMBDA本身处理!
    执行人提交(()->{
    布尔值=真;
    while(值)
    抛出新异常();
    });
    
    通过这些示例,可以更容易地观察到第一个转换为
    可调用的
    ,而第二个转换为
    可运行的
    的原因是编译器推断

    在这两种情况下,lambda主体都是,因为块中的每个return语句的形式都是
    return

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

  • 检测lambda中的所有执行路径声明抛出(从现在起,我们将称为“异常”,仅表示“已检查的异常”)。这包括调用任何声明抛出异常的方法,以及显式调用
    thrownew()
  • 正确地得出结论,lambda的整个主体相当于一个声明抛出异常的代码块;当然,必须处理或重新抛出
  • 由于lambda没有处理异常,因此编译器默认假定必须重新引发这些异常
  • 安全地推断,此lambda必须与功能接口匹配,不能正常完成
  • ,因此是
  • 由于
    Callable
    Runnable
    是此lambda的潜在匹配项,因此编译器选择最具体的匹配项(以覆盖所有场景);它是
    Callable
    ,将lambda转换为它的实例,并创建对
    submit(Callable)
    重载方法的调用引用
  • 而在第二种情况下,编译器执行以下操作:

  • 检测lambda中可能存在未声明抛出异常的执行路径(取决于待评估逻辑)
  • 由于并非所有的执行路径都声明抛出异常,编译器得出结论,lambda的主体不一定等同于声明抛出异常的代码块,编译器不关心/注意代码的某些部分是否声明它们可能抛出异常,仅当整个主体不这样做时
  • 安全地推断lambda不是;因为它可以正常完成
  • 选择
    Runnable
    (因为它是lambda转换为的唯一可用的拟合功能接口),并创建对
    submit(Runnable)
    重载方法的调用引用。所有这些都是以委托给用户为代价的,处理任何抛出的
    异常的责任可能发生在lambda主体的部分内
  • 这是一个很好的问题-我有很多乐趣追问它,谢谢

    简略地
    ExecutorService
    同时具有
    submit(可调用)
    submit(可运行)
    方法

  • 在第一种情况下(使用
    while(true)
    ),
    submit(Callable)
    submit(Runnable)
    都匹配,因此编译器必须在两者之间进行选择
    • submit(Callable)
      被选择在
      submit(Runnable)
      之上,因为
      Callable
      Runnable
      更具体
    • Callable
      call()
      中有
      抛出异常
      ,因此不需要捕获其中的异常
  • 在第二种情况下(使用
    while(tasksObserving)
    ),只有
    submit(Runnable)
    匹配,因此
    ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.submit(() -> {
            while(true)
            {
                //DO SOMETHING
                Thread.sleep(5000);
            }
        });
    
    ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.submit(() -> {
            while(tasksObserving)
            {
                //DO SOMETHING
                Thread.sleep(5000);
            }
        });