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

三世轮回 提交于 2019-11-27 09:57:45

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

Synchronized methods are a shorthand for a method which behaves as if the entire body is enclosed in a synchronized block 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 synchronized methods are entirely a syntactic optimization; they're not needed, they're just more compact than the corresponding synchronized block. 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.)

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.)

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 synchronized keyword on methods implicitly assumes this pattern.

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.

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 ?

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!