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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23453568/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 23:04:39  来源:igfitidea点击:

What is the reason why “synchronized” is not allowed in Java 8 interface methods?

javajava-8synchronizeddefault-methodjsr335

提问by Lukas Eder

In Java 8, I can easily write:

在 Java 8 中,我可以轻松地编写:

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

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

I will get the full synchronisation semantics that I can use also in classes. I cannot, however, use the synchronizedmodifier on method declarations:

我将获得我也可以在类中使用的完整同步语义。但是,我不能synchronized在方法声明上使用修饰符:

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

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

Now, one can argue that the two interfaces behave the same way except that Interface2establishes a contracton method1()and on method2(), which is a bit stronger than what Interface1does. Of course, we might also argue that defaultimplementations should not make any assumptions about concrete implementation state, or that such a keyword simply wouldn't pull its weight.

现在,人们可以争辩说,这两个接口的行为方式相同,只是在 on和 on 上Interface2建立了一个契约,这比什么强一点。当然,我们也可能会争辩说,实现不应该对具体的实现状态做出任何假设,或者这样的关键字根本不会发挥作用。method1()method2()Interface1default

Question:

题:

What is the reason why the JSR-335 expert group decided not to support synchronizedon interface methods?

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

采纳答案by Brian Goetz

While at first it might seem obvious that one would want to support the synchronizedmodifier on default methods, it turns out that doing so would be dangerous, and so was prohibited.

虽然起初似乎很明显人们希望支持synchronized默认方法的修饰符,但事实证明这样做是危险的,因此被禁止。

Synchronized methods are a shorthand for a method which behaves as if the entire body is enclosed in a synchronizedblock whose lock object is the receiver. It might seem sensible to extend this semantics to default methods as well; after all, they are instance methods with a receiver too. (Note that synchronizedmethods are entirely a syntactic optimization; they're not needed, they're just more compact than the corresponding synchronizedblock. There's a reasonable argument to be made that this was a premature syntactic optimization in the first place, and that synchronized methods cause more problems than they solve, but that ship sailed a long time ago.)

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

So, why are they dangerous? Synchronization is about locking. Locking is about coordinating shared access to mutable state. Each object should have a synchronization policy that determines which locks guard which state variables. (See Java Concurrency in Practice, section 2.4.)

那么,为什么它们是危险的?同步与锁定有关。锁定是关于协调对可变状态的共享访问。每个对象都应该有一个同步策略来确定哪些锁保护哪些状态变量。(请参阅Java 并发实践,第 2.4 节。)

Many objects use as their synchronization policy the Java Monitor Pattern(JCiP 4.1), in which an object's state is guarded by its intrinsic lock. There is nothing magic or special about this pattern, but it is convenient, and the use of the synchronizedkeyword on methods implicitly assumes this pattern.

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

It is the class that owns the state that gets to determine that object's synchronization policy. But interfaces do not own the state of the objects into which they are mixed in. So using a synchronized method in an interface assumes a particular synchronization policy, but one which you have no reasonable basis for assuming, so it might well be the case that the use of synchronization provides no additional thread safety whatsoever (you might be synchronizing on the wrong lock). This would give you the false sense of confidence that you have done something about thread safety, and no error message tells you that you're assuming the wrong synchronization policy.

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

It is already hard enough to consistently maintain a synchronization policy for a single source file; it is even harder to ensure that a subclass correctly adhere to the synchronization policy defined by its superclass. Trying to do so between such loosely coupled classes (an interface and the possibly many classes which implement it) would be nearly impossible and highly error-prone.

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

Given all those arguments against, what would be the argument for? It seems they're mostly about making interfaces behave more like traits. While this is an understandable desire, the design center for default methods is interface evolution, not "Traits--". Where the two could be consistently achieved, we strove to do so, but where one is in conflict with the other, we had to choose in favor of the primary design goal.

鉴于所有这些反对的论点,论据是什么?似乎他们主要是为了让界面表现得更像特征。虽然这是一个可以理解的愿望,但默认方法的设计中心是界面演变,而不是“特性--”。在两者可以一致实现的地方,我们努力做到这一点,但在一个与另一个冲突的地方,我们不得不选择支持主要设计目标。

回答by zhenke zhu

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();
}
}

result:

结果:

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

(sorry for using parent class as example)

(抱歉以父类为例)

from the result , we could know that the parent class lock is owned by every sub class, SonSync1 and SonSync2 object have different object lock . every lock is independency. so in this case , i think it is not dangerous using a synchronized in a parent class or a common interface . could anyone explain more on this ?

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