[java] Java 8 인터페이스 메소드에서 “동기화”가 허용되지 않는 이유는 무엇입니까?

Java 8에서는 다음과 같이 쉽게 작성할 수 있습니다.

interface Interface1 {
    default void method1() {
        synchronized (this) {
            // Something
        }
    }

    static void method2() {
        synchronized (Interface1.class) {
            // Something
        }
    }
}

클래스에서도 사용할 수있는 전체 동기화 의미론을 얻습니다. 그러나 synchronized메소드 선언에 수정자를 사용할 수는 없습니다 .

interface Interface2 {
    default synchronized void method1() {
        //  ^^^^^^^^^^^^ Modifier 'synchronized' not allowed here
    }

    static synchronized void method2() {
        // ^^^^^^^^^^^^ Modifier 'synchronized' not allowed here
    }
}

이제 한 것을 제외하고 같은 방식의 행동이 인터페이스를 주장 할 수 Interface2설정 계약method1()와에 대한 method2()것보다 조금 더 강한, Interface1수행합니다. 물론 우리는 default구현이 구체적인 구현 상태에 대해 어떤 가정도해서는 안되거나 그러한 키워드가 단순히 무게를 풀지 않을 것이라고 주장 할 수 있습니다 .

질문:

JSR-335 전문가 그룹이 synchronized인터페이스 방법 을 지원하지 않기로 결정한 이유는 무엇입니까 ?



답변

처음에는 synchronized기본 메소드 에서 수정자를 지원하려는 것이 분명해 보이지만 그렇게하는 것은 위험하므로 금지 된 것으로 나타났습니다.

동기화 된 메소드는 전체 본문이 synchronized잠금 오브젝트가 수신자 인 블록에 포함 된 것처럼 작동하는 메소드의 약어입니다 . 이 의미론을 기본 메소드로 확장하는 것이 합리적으로 보일 수 있습니다. 결국, 그들은 수신자와 함께 인스턴스 메소드입니다. ( synchronized메소드는 전적으로 구문 최적화입니다. 필요하지는 않으며, 해당 synchronized블록 보다 더 작습니다 .) 이는 조기에 구문 최적화였으며, 동기화 된 메소드라는 합리적인 주장이 있습니다. 그들이 해결하는 것보다 더 많은 문제를 일으키지 만, 그 배는 오래 전에 항해했습니다.)

그래서 왜 위험한가요? 동기화는 잠금에 관한 것입니다. 잠금은 공유 가능한 액세스를 변경 가능한 상태로 조정하는 것입니다. 각 객체에는 어떤 잠금이 어떤 상태 변수를 보호하는지 결정하는 동기화 정책이 있어야합니다. ( 실제 Java Concurrency , 2.4 단원을 참조하십시오 .)

많은 객체가 동기화 정책으로 Java Monitor Pattern (JCiP 4.1)을 사용하며,이 객체에서 객체의 상태는 본질적 잠금으로 보호됩니다. 이 패턴에 대한 마법이나 특별한 것은 없지만 편리하고 synchronized메서드에 키워드를 사용하면 암시 적 으로이 패턴을 가정합니다.

해당 객체의 동기화 정책을 결정하는 상태를 소유하는 클래스입니다. 그러나 인터페이스는 혼합 된 오브젝트의 상태를 소유하지 않습니다. 따라서 인터페이스에서 동기화 된 메소드를 사용하는 것은 특정 동기화 정책을 가정하지만 가정 할만한 합리적인 근거가없는 정책을 가정하므로 그럴 수도 있습니다. 동기화를 사용하면 추가 스레드 안전성이 제공되지 않습니다 (잘못된 잠금에서 동기화 중일 수 있음). 이것은 스레드 안전성에 대해 무언가를했다는 잘못된 확신을 제공하며, 잘못된 동기화 정책을 가정하고 있다는 오류 메시지가 표시되지 않습니다.

단일 소스 파일에 대한 동기화 정책을 일관되게 유지하기에는 이미 어렵습니다. 서브 클래스가 자신의 수퍼 클래스에 의해 정의 된 동기화 정책을 올바르게 준수하는지 확인하기가 더 어렵습니다. 이렇게 느슨하게 결합 된 클래스 (인터페이스와이를 구현하는 많은 클래스)간에 그렇게하는 것은 거의 불가능하고 오류가 발생하기 쉽습니다.

이러한 모든 주장에 대하여, 어떤 주장이 있을까요? 인터페이스가 특성처럼 동작하도록 만드는 데 주로 사용되는 것 같습니다. 이것은 이해할 수있는 욕구이지만 기본 방법의 디자인 센터는 “Traits–“가 아니라 인터페이스 진화입니다. 이 두 가지를 일관되게 달성 할 수있는 방법을 찾기 위해 노력했지만, 하나가 다른 것과 충돌하는 경우 기본 설계 목표에 찬성하여 선택해야했습니다.


답변

public class ParentSync {

public synchronized void parentStart() {
    System.out.println("I am " + this.getClass() + " . parentStarting. now:" + nowStr());
    try {
        Thread.sleep(30000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("I am " + this.getClass() + " . parentFinished. now" + nowStr());
}

private String nowStr() {
    return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
}
}


public class SonSync1 extends ParentSync {
public void sonStart() {
    System.out.println("I am " + this.getClass() + ". sonStarting,calling parent now ... ");
    super.parentStart();
    System.out.println("I am " + this.getClass() + ". sonFinished");
}
}



public class SonSync2 extends ParentSync {

public void sonStart() {
    System.out.println("I am " + this.getClass() + ". sonStarting,calling parent now ... ");
    super.parentStart();
    System.out.println("I am " + this.getClass() + ". sonFinished");
}
}



public class SyncTest {
public static void main(String[] args) throws Exception {

    new Thread(() -> {
        new SonSync1().sonStart();
    }).start();

    new Thread(() -> {
        new SonSync2().sonStart();
    }).start();

    System.in.read();
}
}

결과:

I am class com.common.interface18_design.whynotsync_onmethod.SonSync1. sonStarting,calling parent now ... 
I am class com.common.interface18_design.whynotsync_onmethod.SonSync2. sonStarting,calling parent now ... 
I am class com.common.interface18_design.whynotsync_onmethod.SonSync2 . parentStarting. now:2019-04-18 09:50:08
I am class com.common.interface18_design.whynotsync_onmethod.SonSync1 . parentStarting. now:2019-04-18 09:50:08
I am class com.common.interface18_design.whynotsync_onmethod.SonSync1 . parentFinished. now2019-04-18 09:50:38
I am class com.common.interface18_design.whynotsync_onmethod.SonSync1. sonFinished
I am class com.common.interface18_design.whynotsync_onmethod.SonSync2 . parentFinished. now2019-04-18 09:50:38
I am class com.common.interface18_design.whynotsync_onmethod.SonSync2. sonFinished

(부모 클래스를 예로 사용하여 죄송합니다)

그 결과 부모 클래스 잠금이 모든 하위 클래스에 의해 소유되고 SonSync1과 SonSync2 객체가 다른 객체 잠금을 가짐을 알 수있었습니다. 모든 자물쇠는 독립성입니다. 따라서이 경우 부모 클래스 또는 공통 인터페이스에서 동기화를 사용하는 것이 위험하지 않다고 생각합니다. 누구든지 이것에 대해 더 설명 할 수 있습니까?


답변