В чем причина, почему "синхронизация" не допускается в методах интерфейса 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 методы интерфейса?

Ответы

Ответ 1

Хотя на первый взгляд может показаться очевидным, что можно хотеть поддерживать synchronized модификатор в методах по умолчанию, оказывается, что это было бы опасно и поэтому было запрещено.

Синхронизированные методы - это сокращение для метода, который ведет себя так, как будто все тело заключено в synchronized блок, чей объект блокировки является получателем. Может показаться целесообразным расширить эту семантику и на методы по умолчанию; в конце концов, они тоже методы экземпляра с получателем. (Обратите внимание, что synchronized методы являются полностью синтаксической оптимизацией; они не нужны, они просто более компактны, чем соответствующий synchronized блок. Есть разумный аргумент, что это была преждевременная синтаксическая оптимизация в первую очередь, и что синхронизированные методы вызывают больше проблем, чем решают, но этот корабль давно отплыл.)

Итак, почему они опасны? Синхронизация о блокировке. Блокировка - это координация общего доступа к изменяемому состоянию. Каждый объект должен иметь политику синхронизации, которая определяет, какие блокировки защищают, какие переменные состояния. (См. Java Concurrency на практике, раздел 2.4.)

Многие объекты используют в качестве своей политики синхронизации шаблон монитора Java (JCiP 4.1), в котором состояние объекта охраняется его внутренней блокировкой. В этом шаблоне нет ничего волшебного или особенного, но он удобен, и использование ключевого слова synchronized в методах неявно предполагает этот шаблон.

Это класс, которому принадлежит состояние, которое определяет политику синхронизации этого объекта. Но интерфейсам не принадлежит состояние объектов, в которые они смешаны. Поэтому использование синхронизированного метода в интерфейсе предполагает определенную политику синхронизации, но такую, которую у вас нет разумных оснований для принятия, поэтому вполне может быть так, что использование синхронизации не обеспечивает никакой дополнительной безопасности потока (вы можете синхронизироваться с неправильной блокировкой). Это даст вам ложное чувство уверенности в том, что вы что-то сделали с безопасностью потоков, и ни одно сообщение об ошибке не сообщит вам, что вы принимаете неправильную политику синхронизации.

Уже достаточно сложно постоянно поддерживать политику синхронизации для одного исходного файла; еще труднее обеспечить, чтобы подкласс правильно придерживался политики синхронизации, определенной его суперклассом. Попытка сделать это между такими слабо связанными классами (интерфейсом и, возможно, многими классами, которые его реализуют) была бы почти невозможной и подверженной ошибкам.

Учитывая все эти аргументы против, для чего будет аргумент? Кажется, они в основном делают интерфейсы более похожими на черты. Хотя это понятное желание, центром разработки методов по умолчанию является эволюция интерфейса, а не "Traits--". В тех случаях, когда эти два фактора могли быть последовательно достигнуты, мы стремились к этому, но в тех случаях, когда один конфликтует с другим, нам приходилось выбирать в пользу основной цели проектирования.

Ответ 2

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 имеют различную блокировку объекта. каждый замок независим. так что в этом случае, я думаю, не опасно использовать синхронизированный в родительском классе или общий интерфейс. Кто-нибудь может объяснить больше об этом?