3

对不起,伙计们,但我不明白,在哪个对象是synchronized块内部sync()方法是同步的:

public class TestLambda {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    sync();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }

    static void sync() throws InterruptedException {
        synchronized ((Runnable)TestLambda::new) {
            System.out.println("inside");
            Thread.sleep(1000L);
        }
    }
}

如果在方法引用对象上,为什么我不能只写: synchronized (TestLambda::new)?(这将是编译时错误)。

有任何想法吗?

UPD:以防万一:它确实是同步的

UPD-2:对于那些怀疑的人,简单的例子:

C:\sandbox\research\learn8\src>"C:\Program Files\Java\jdk1.8.0_31\bin\"javac TestLambda.java
TestLambda.java:27: error: method reference not expected here
    public class Test { { synchronized (Test::new) { System.out.println("sync"); } } }
                                        ^
1 error
4

2 回答 2

3

让我们看看下面的 2 个作业:

Supplier<TestLambda> supp = TestLambda::new;
Runnable runnable = TestLambda::new;

它们都编译得很好。基本上是因为 lambda 或方法引用可以兼容多个功能接口。这意味着,仅仅编写TestLambda::new并不能告诉我们所创建对象的确切运行时类型。实例化哪个接口取决于目标类型。并且该目标类型应始终是功能接口,而以下语句中并非如此:

synchronized(TestLambda::new) {
}

因此,编译器不允许这样做,因为运行时将无法决定要实例化哪个功能接口。您通过将方法引用转换为 来提供该信息Runnable。所以,在下面的声明中:

synchronized((Runnable) TestLambda::new) {
}

运行时将实例化实现Runnable接口的类的对象。从某种意义上说,铸造赋予了方法参考的具体性。

为了给出一个模糊的想法,这可以翻译成这样:

class RuntimeClass implements Runnable {
    public void run() {
        TestLambda testLambda = new TestLambda();
    }
}

synchronized(new RuntimeClass()) {
}

PS:将是单例的实际实例RuntimeClass(因为我们使用的是无状态方法表达式)——我原来的错误陈述

PPS:正如@Stuart 的评论所指出的,不能保证对于 lambda 或方法引用,是否会创建一个新实例或返回相同的实例。所以,你不应该对它们进行同步。

于 2015-04-03T20:37:46.943 回答
3

JLS 14.19。synchronized声明_

SynchronizedStatement:
    synchronized ( Expression ) Block

Expression的类型必须是引用类型,否则会发生编译时错误。


JLS 15.13.2。方法引用的类型

如果 T 是函数接口类型(第 9.8 节)并且表达式与从 T 派生的基本目标类型的函数类型一致,则方法引用表达式在赋值上下文、调用上下文或转换上下文中与目标类型 T 兼容.

上面的两个规定应该可以描绘:synchronized语句采用任何引用类型的表达式,并且方法引用与作为函数接口的任何目标引用类型 T兼容。注意Object不满足这个。

换句话说,还有一个自由度:方法引用应该与哪种引用类型兼容?这种自由必须被显式类型转换带走,强制转换为特定类型。只有这样才能知道整个表达式的类型。

于 2015-04-03T20:46:38.377 回答