同步表达式中的方法引用

Method reference inside synchronized expression

抱歉,伙计们,但我不明白,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

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

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

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

synchronized(TestLambda::new) {
}

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

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

运行时将实例化一个实现Runnable接口的class对象。从某种意义上说,转换为方法引用提供了具体性。

为了给出一个模糊的概念,可以这样翻译:

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

synchronized(new RuntimeClass()) {
}

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

P.P.S:正如@Stuart 的评论所述,不能保证对于 lambda 或方法引用,是否会创建一个新实例或同样会被退回。所以,你不应该同步它们。

JLS 14.19. The synchronized Statement

SynchronizedStatement:
    synchronized ( Expression ) Block

The type of Expression must be a reference type, or a compile-time error occurs.


JLS 15.13.2. Type of a Method Reference

A method reference expression is compatible in an assignment context, invocation context, or casting context with a target type T if T is a functional interface type (§9.8) and the expression is congruent with the function type of the ground target type derived from T.

上面的两个规定应该是这样的:synchronized 语句采用任何引用类型的表达式,方法引用与任何目标引用类型 T 兼容这是一个功能接口。请注意 Object 不满足此要求。

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