为什么我们不能在Lambda函数内部直接调用Thread#sleep()?

时间:2018-10-02 06:24:06

标签: java multithreading lambda java-8

8 个答案:

答案 0 :(得分:83)

Thread.sleepThread类中的静态方法。

在匿名类中无需任何限定符就可以直接调用sleep的原因是,因为您实际上处于从Thread继承的类的上下文中。因此,sleep可以在此处访问。

但是在lambda情况下,您不在从Thread继承的类中。您位于该代码所在的任何类中。因此,不能直接调用sleep,而您需要说Thread.sleepdocumentation也支持:

  

Lambda表达式具有词法范围。这意味着他们不   从超类型继承任何名称或引入新的级别   范围界定。 Lambda表达式中的声明与   它们在封闭的环境中。

基本上就是说,在lambda内,您实际上处于与在lambda之外相同的范围。如果您无法在lambda之外访问sleep,那么也不能在其内部访问。

此外,请注意,这里显示的创建线程的两种方式本质上是不同的。在lambda中,您将Runnable传递给Thread构造函数,而在匿名类中,您是通过直接创建匿名类来创建Thread

答案 1 :(得分:18)

第一种方法是将Runnable传递到Thread,需要调用Thread.sleep

Thread t2 = new Thread(() -> {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
    }
});

它是以下内容的简称:

Runnable runnable = new Runnable() {
    public void run(){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {}
    }
};

Thread t2 = new Thread(runnable);

在第二个过程中,您将直接覆盖thread.run方法,因此可以在thread.sleep中调用thread.run

答案 2 :(得分:10)

这最终是对范围的误解。

在将lambda传递给线程时,您不是在创建Thread的子类,而是在传递Runnable的FunctionalInterface并调用Thread的构造函数。尝试调用Sleep时,作用域的上下文是Runnable +类的组合(如果Runnable接口包含默认方法,则可以调用默认方法),而不是Thread。

Runnable没有定义sleep(),但是Thread定义了。

创建匿名内部类时,您将线程化为子类,因此sleep()可供您调用,因为Scope的上下文是Thread的子类。

由于这种误解,不建议使用没有类名的静态方法。使用Thread.Sleep既正确又在所有情况下都是明确的。

答案 3 :(得分:7)

您的疑问源自对如何定义lambda表达式的作用域和匿名类的误解。下面,我将尝试澄清这一点。

Lambda表达式不会引入新的作用域范围。这意味着,在它内部,您只能访问与在直接封闭的代码块中可以访问的相同的东西。看看docs怎么说:

  

Lambda表达式具有词法范围。这意味着他们不   从超类型继承任何名称或引入新的级别   范围界定。 Lambda表达式中的声明与   它们在封闭的环境中。

匿名类的工作方式不同。他们确实引入了新的作用域范围。它们的行为很像local class(您在代码块中声明的类),尽管它们没有构造函数。看看docs怎么说:

  

类似于本地类,匿名类可以捕获变量;它们对包含范围的局部变量具有相同的访问权限:

     
      
  • 匿名类可以访问其封闭类的成员。
  •   
  • 匿名类无法在其封闭范围内访问未声明为final或有效地为final的局部变量。
  •   
  • 就像嵌套类一样,匿名类中的类型声明(例如变量)会遮盖住封装中的其他任何声明   具有相同名称的作用域。有关更多信息,请参见阴影。
  •   

在这种情况下,匿名类将像Thread内的本地类一样起作用,因此,由于此方法在其范围之内,因此它将能够直接访问sleep()。但是,在lambda表达式中,sleep()不在其范围内(您不能在封闭环境中调用sleep()),因此必须使用Thread.sleep()。请注意,此方法是 static ,因此不需要调用其类的实例。

答案 4 :(得分:5)

以下代码有效:

    Thread t2 = new Thread(() -> {
        try { 
            Thread.sleep(1000);
        } 
        catch (InterruptedException e) {}
    });

这是因为在创建sleep(int milliseconds)实例并将其传递给Thread类构造函数时,RunnableThread类的方法。

在第二种方法中,您正在创建Thread类的匿名内部类实例,因此可以访问所有Thread类方法。

答案 5 :(得分:4)

我喜欢所提供和接受的答案,但是用简单得多的话来说,您可以认为this已从匿名内部类更改为lambda。

对于AIC,this指您要扩展的类的实例(在您的示例中为Thread),对于lambda expression,{{1} }指的是包围lambda表达式的类的实例(无论您的示例中是哪个类)。我敢打赌,在您使用lambda表达式的课堂上,还没有这样的this定义。

答案 6 :(得分:2)

public void foo() {
    new Thread(() -> { sleep(1000); });
}

等同于

public void foo() {
    new Thread(this::lambda$0);
}
private void lambda$0() {
    sleep(1000);
}

因此编译器将不会在sleep中查找Thread

答案 7 :(得分:-2)

Thread.sleep是静态方法...

 Thread t2 = new Thread(() -> {
        try {
            Thread.sleep(1000);
        }
        catch (InterruptedException e) {}
    });