在Java 8接口方法中不允许“synchronized”的原因是什么?

时间:2014-05-04 07:06:01

标签: java java-8 synchronized default-method jsr335

在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
    }
}

现在,人们可以争辩说两个接口的行为方式相同,只是Interface2method1()method2()上建立了合同,这是一个比Interface1更强大。当然,我们也可能认为default实现不应该对具体实现状态做出任何假设,或者这样的关键字根本不会减轻它的重量。

问题:

JSR-335专家组决定不在接口方法上支持synchronized的原因是什么?

2 个答案:

答案 0 :(得分:255)

虽然起初看起来似乎很明显需要在默认方法上支持synchronized修饰符,但事实证明这样做会很危险,因此被禁止。

同步方法是一种方法的简写,其行为就像整个主体被包含在其锁定对象是接收器的synchronized块中一样。将此语义扩展到默认方法似乎也是明智的;毕竟,它们也是带接收器的实例方法。 (请注意,synchronized方法完全是语法优化;它们不需要,它们比相应的synchronized块更紧凑。有一个合理的论据是首先,这是一个过早的句法优化,并且同步方法会导致比他们解决的问题更多的问题,但是这艘船早已航行。)

那么,他们为什么危险?同步是关于锁定。锁定是关于协调对可变状态的共享访问。每个对象都应该有一个同步策略,用于确定哪些锁保护哪些状态变量。 (参见Java Concurrency in Practice,第2.4节。)

许多对象使用 Java Monitor Pattern (JCiP 4.1)作为其同步策略,其中对象的状态由其内部锁保护。这种模式没有任何魔力或特殊之处,但它很方便,并且在方法上使用synchronized关键字隐含地假设了这种模式。

拥有该状态的类可以确定该对象的同步策略。但是接口不拥有混合它们的对象的状态。因此,在接口中使用synchronized方法假定一个特定的同步策略,但是你没有合理的假设基础,所以很可能是使用同步不会提供额外的线程安全性(您可能在错误的锁上进行同步)。这会给你一种错误的信心,你已经对线程安全做了一些事情,并且没有错误消息告诉你你正在假设错误的同步策略。

为单个源文件持续维护同步策略已经足够困难了;确保子类正确遵守其超类定义的同步策略更加困难。在这种松散耦合的类(一个接口和可能实现它的许多类)之间尝试这样做几乎是不可能的,并且非常容易出错。

鉴于所有这些论点反对,会有什么争论?似乎他们主要是关于使界面表现得更像特质。虽然这是一个可以理解的愿望,但默认方法的设计中心是界面演化,而不是" Traits - "。在两者可以持续实现的情况下,我们努力做到这一点,但如果一方与另一方发生冲突,我们必须选择支持主要的设计目标。

答案 1 :(得分:0)

CustomWebApplicaitonFactory

结果:

app.UseDeveloperExceptionPage();

(抱歉使用父类作为示例)

从结果中,我们可以知道父类锁由每个子类拥有,SonSync1和SonSync2对象具有不同的对象锁。每个锁都是独立的。所以在这种情况下,我认为在父类或通用接口中使用同步并不危险。有人可以解释更多吗?