在 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
}
}
现在,人们可以争辩说这两个接口(interface)的行为方式相同,只是 Interface2
在 method1()
和 上建立了 contract code>method2()
,比Interface1
做的要强一些。当然,我们也可能会争辩说,default
实现不应该对具体的实现状态做出任何假设,或者这样的关键字根本不会发挥作用。
问题:
JSR-335 专家组决定在接口(interface)方法上不支持 synchronized
的原因是什么?
最佳答案
虽然一开始似乎很明显希望在默认方法上支持 synchronized
修饰符,但事实证明这样做很危险,因此被禁止。
同步方法是一种方法的简写,其行为就像整个主体都包含在一个 synchronized
block 中,该 block 的锁对象是接收者。将这种语义扩展到默认方法似乎也是明智的;毕竟,它们也是带有接收器的实例方法。 (请注意,synchronized
方法完全是一种语法优化;它们不是必需的,它们只是比相应的 synchronized
block 更紧凑。有一个合理的论据要提出这首先是一个过早的句法优化,同步方法导致的问题比解决的问题多,但那艘船很久以前就航行了。)
那么,为什么它们很危险?同步是关于锁定的。锁定是关于协调对可变状态的共享访问。每个对象都应该有一个同步策略来确定哪些锁保护哪些状态变量。 (参见 Java Concurrency in Practice,第 2.4 节。)
许多对象使用 Java Monitor Pattern (JCiP 4.1) 作为它们的同步策略,其中对象的状态由其内在锁保护。这种模式没有什么神奇或特别之处,但它很方便,并且在方法上使用 synchronized
关键字隐含地假定了这种模式。
拥有状态的类可以确定该对象的同步策略。但是接口(interface)不拥有它们所混入的对象的状态。因此,在接口(interface)中使用同步方法假定了一个特定的同步策略,但是你没有合理的假设基础,所以很可能是这样的情况同步的使用不会提供任何额外的线程安全性(您可能在错误的锁上同步)。这会给你一种错误的自信感,认为你已经做了一些关于线程安全的事情,并且没有错误消息告诉你你假设了错误的同步策略。
一致地维护单个源文件的同步策略已经足够困难了;更难确保子类正确遵守其父类(super class)定义的同步策略。试图在这种松散耦合的类(一个接口(interface)和可能实现它的许多类)之间这样做几乎是不可能的并且非常容易出错。
考虑到所有这些反对的论点,你的论点是什么?似乎它们主要是关于使接口(interface)的行为更像特征。虽然这是一个可以理解的愿望,但默认方法的设计中心是接口(interface)演变,而不是“Traits--”。在两者可以始终如一地实现的情况下,我们努力做到这一点,但在一个与另一个发生冲突的情况下,我们必须选择有利于主要设计目标。
关于java - Java 8接口(interface)方法中不允许 “synchronized”是什么原因?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23453568/