Lambda中无限while循环内的Thread.sleep不需要'catch(InterruptedException)'-为什么不呢? [英] Thread.sleep inside infinite while loop in lambda doesn't require 'catch (InterruptedException)' - why not?

查看:404
本文介绍了Lambda中无限while循环内的Thread.sleep不需要'catch(InterruptedException)'-为什么不呢?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题是关于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:

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

  1. 可致电:V call() throws Exception;
  2. 可运行:public abstract void run();
  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();
    });

在这些示例中,可能更容易观察到第一个被转换为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:

  1. 检测到lambda中的所有执行路径都声明抛出已检查的异常(从现在开始,我们将其称为'exception',仅表示'checked exceptions').这包括调用任何声明抛出异常的方法,以及对throw new <CHECKED_EXCEPTION>()的显式调用.
  2. 正确推断lambda的 WHOLE 主体等效于声明抛出异常的代码块;当然,必须必须:处理或重新抛出.
  3. 由于lambda不处理异常,因此编译器默认情况下假定必须重新抛出这些异常.
  4. 安全地推断此lambda必须与功能接口匹配,不能complete normally,因此为
  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 re-thrown.
  3. Since the lambda is not handling the exception, then the compiler defaults to assume that these exception(s) must be re-thrown.
  4. Safely infers that this lambda must match a functional interface cannot complete normally and therefore is value-compatible.
  5. Since Callable<?> and Runnable are potential matches for this lambda, the compiler selects the most specific match (to cover all scenarios); which is the Callable<?>, converting the lambda into an instance of it and creating an invocation reference to the submit(Callable<?>) overloaded method.

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

While, in the second case, the compiler does the following:

  1. 检测到lambda中可能存在请勿声明抛出异常的执行路径(取决于要评估的逻辑).
  2. 由于并非所有执行路径都声明抛出异常,因此编译器得出结论,lambda的主体与声明抛出异常的代码块 NOT NECESSARILY 等效-编译器不在乎/注意是否该代码的某些部分确实声明了它们可以(无论整个主体是否这样做).
  3. 安全地推断出lambda不是
  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. Safely infers that the lambda is not value-compatible; since it MAY complete normally.
  4. Selects Runnable (as it is the only available 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.

这是一个很好的问题-追逐它,我很开心,谢谢!

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

这篇关于Lambda中无限while循环内的Thread.sleep不需要'catch(InterruptedException)'-为什么不呢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆