Синхронный метод Java

Рассмотрим этот код:

public synchronized void onSignalsTimeout(List<SignalSpec> specs) {
    if (specs != null && specs.size() > 0) {
        for (SignalSpec spec : specs) {
            ParsedCANSignal timeoutedSignal = new ParsedCANSignal();
            SignalsProvider.getInstance().setSignal(spec.name, spec.parent.parent.channel, timeoutedSignal);
        }
    }
}

У меня есть простой вопрос: Когда Thread 1 вызывает метод onSignalsTimeout, могут ли объекты доступа Thread 2, к которым обращаются в этом методе?

Невозможно найти место, если "synchronized" блокирует только доступ к этому методу или доступ ко всем объектам, используемым в этом методе.

Ответы

Ответ 1

Прежде всего, забудьте о синхронизированных методах. Так называемый синхронизированный метод...

synchronized AnyType foobar(...) {
    doSomething();
}

Это не что иное, как быстрый способ записи:

AnyType foobar(...) {
    synchronized(this) {
        doSomething();
    }
}

В этом случае нет ничего особенного в этом методе. Особенностью является синхронизированный блок, и что делает синхронизированный блок очень простым. Когда JVM выполняет это:

synchronized(foo) {
    doSomething();
}

Сначала он вычисляет выражение foo. Результат должен быть ссылкой на объект. Затем он блокирует объект, выполняет тело блока synchronized, а затем он разблокирует объект.

Но что заперто? Это может означать меньше, чем вы думаете. Это не мешает другим потокам использовать объект. Это не мешает им получать доступ к полям объекта или, обновляя его поля. Единственное, что предотвращает блокирование объекта, - это предотвращение одновременного блокирования других потоков одним и тем же объектом.

Если поток A пытается ввести synchronized(foo) {...}, тогда как поток B уже заблокирован foo (либо в том же блоке synchronized, либо в другом), тогда поток A будет вынужден ждать, пока поток B не освободит блокировку.


Для защиты данных используются блоки synchronized.

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

Предположим, что поток не может изменить данные из одного действительного состояния в другое действительное состояние без временного создания недопустимого состояния.

Если вы поместите код, который изменяет состояние в блоке synchronized(foo), и вы помещаете каждый блок кода, который может видеть состояние в синхронизированный блок, который блокирует один и тот же объект, foo, тогда вы предотвратите другие потоки от просмотра временного недействительного состояния.

Ответ 2

Да, другие потоки могут обращаться к объектам, используемым в методе; ключевое слово synchronized гарантирует, что не более одного потока в то время может выполнить код метода.

Из https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html:

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

Ответ 3

В этом контексте synchronized одновременно блокирует этот метод и любой другой метод, аналогично помеченный как synchronized в вашем классе.