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;

どちらも正常にコンパイルされます。基本的に、ラムダまたはメソッド参照は複数の機能インターフェースと互換性があるためです。つまり、単に記述しただけ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 のコメントから指摘されているように、ラムダ参照またはメソッド参照の場合、新しいインスタンスが作成されるか、同じものが返されるかは保証されません。したがって、それらを同期するべきではありません。

于 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 と互換性があります。 .

上記の 2 つの規定は全体像を示しているはずです。synchronizedステートメントは任意の参照型の式を取り、メソッド参照は関数型インターフェイスである任意のターゲット参照型 T と互換性があります。Objectはこれを満たさないことに注意してください。

言い換えれば、自由度が残っています: メソッド参照は正確にどの参照型と互換性があるべきでしょうか? この自由は、明示的な型キャストによって取り除かれる必要があります。これにより、その特定の型に強制的に入れられます。そうして初めて、式全体の型がわかります。

于 2015-04-03T20:46:38.377 に答える