Синхронизированный блок в каждом методе, который использует поле

Это, кажется, довольно частый вопрос, хотя я его не нашел. Предположим, у меня есть эта часть кода:

public class MyClass {
    private AnotherClass mField;

    public void changeOne(AnotherClass newOne) {
       //<...> lines of code here
        synchronized (mField) {
            mField = newOne;
        }
       //<...> lines of code here

    }

    public void changeTwo(AnotherClass newTwo) {
       //<...> lines of code here
        mField = newTwo;
       //<...> lines of code here
    }
}

Скажем, changeOne() и changeTwo() вызываются из разных потоков. Достаточно ли иметь синхронизированный блок в changeOne() для защиты mField от изменения с помощью changeTwo() ? Или мне нужно явно обернуть каждое место, где mField будет изменен в блок synchronized ? (пожалуйста, оставьте синхронные методы и другие).

1
nl ja de
@TedHopp Я просто имел в виду, что в реальной части кода, которая создала этот вопрос, я имею дело только с блоком synchronized , а не с синхронизированными методами, изменчивыми полями.
добавлено автор Andrey Ermakov, источник
@TedHopp Почему не по переменной?
добавлено автор Andrey Ermakov, источник
Что случилось с синхронизированными методами? И какие «другие» мы должны оставить?
добавлено автор Ted Hopp, источник
Ну, что бы вы ни делали, вам нужно синхронизировать с общим объектом блокировки, а не с изменяемой переменной! (Самый очевидный удобный объект блокировки для управления доступом к полю элемента - this , что в основном является тем, что делает синхронизированный метод).
добавлено автор Ted Hopp, источник
Не для переменной, поскольку после изменения переменной вы больше не синхронизированы с переменной; вы синхронизированы на том, что было переменной. Другие потоки не смогут синхронизировать объект, который вы заблокировали, поэтому фактически не будет синхронизации.
добавлено автор Ted Hopp, источник

2 ответы

Вам нужно явно синхронизировать все изменения с mField с помощью синхронизированного (или) синхронизированного метода. В противном случае более чем один поток может изменить mField , выполнив changeTwo за раз.

EDIT: Как предложил Тедд Хопп, если переменная не является изменчивой, чтение также необходимо синхронизировать, и блокировка, которую вы получаете, должна быть на одном объекте.

4
добавлено
Андрей Эрмаков: Это правильно. Каждый поток создает стек при выполнении метода, поэтому один поток не знает о других изменениях нитей, если вы не синхронизируете.
добавлено автор kosa, источник
@WaleryStrauch: Оба случая.
добавлено автор kosa, источник
@TedHopp: Согласитесь. Отредактированный ответ с вашим комментарием.
добавлено автор kosa, источник
Так changeTwo не будет проверять, заблокирован ли mField без синхронизированного блока вокруг него?
добавлено автор Andrey Ermakov, источник
Недостаточно синхронизировать только изменения. Если чтение не синхронизировано и переменная не объявлена ​​ volatile , то потоки чтения могут использовать потоковые локальные копии устаревшего значения даже после того, как синхронизированный блок выйдет после изменения переменной. См. Рисунок 5 в статье Брайана Гетца Управление волатильностью .
добавлено автор Ted Hopp, источник
@Nambari вы имеете в виду, когда два потока выполняют changeTwo одновременно, или два потока, где первый выполняет changeOne , а второй выполняет changeTwo ?
добавлено автор Walery Strauch, источник

Нет, это не так, обе нити должны пытаться использовать тот же замок, а затем, если Thread A взял блокировку, сначала Thread B будет заблокирован до тех пор, пока A не выпустит его. Блокировка может быть любым объектом, общим для А и В, наиболее типичным для вашего случая является

public class MyClass {
    private AnotherClass mField;

        public synchronized void changeOne(AnotherClass newOne) {
             ...
        }

        public synchronzied void changeTwo(AnotherClass newTwo) {
             ...
        }

в этом случае этот используется как блокировка. Это эквивалентно (почти)

    public void changeOne(AnotherClass newOne) {
         synchronized(this) {
              ...
         }
    }

    public void changeTwo(AnotherClass newOne) {
         synchronized(this) {
              ...
         }
    }

синхронизированный метод более компактен, тогда как синхронизированный блок более гибкий. С синхронизированным блоком вы блокируете любой объект, в то время как синхронизированный метод блокирует неявно либо на this , либо для статических методов в классе class .

0
добавлено
см. мое обновление о разнице
добавлено автор Evgeniy Dorofeev, источник
Согласно раздел 8.4.3.6 спецификации Java Language , синхронизация метода точно (не почти) эквивалентна окружению тела метода с помощью synchronized ( this ) { ...} (по крайней мере, например, методы).
добавлено автор Ted Hopp, источник
pro.jvm
pro.jvm
3 503 участник(ов)

Сообщество разработчиков Java Scala Kotlin Groovy Clojure Чат для нач-их: @javastart Наш сайт: projvm.com projvm.ru Наш канал: @proJVM Вакансии: @jvmjobs Конфы: @jvmconf

Java & Co
Java & Co
2 370 участник(ов)

Можно обсуждать с матом и без всё, что касается жабы, вплоть до холиваров. НЕ ИМЕЕТ ОТНОШЕНИЯ К САЙТУ JAVARUSH.RU ПРАВИЛА - https://t.me/javarush/75723 Вакансии сюда - https://telegram.me/joinchat/B7IzvUCnfo6d8t3yIxKguQ По вопросам - @thedude

learn.java
learn.java
1 888 участник(ов)

Чат для начинающих и не только Статистика: https://combot.org/chat/-1001083535868 Основной чат - @jvmchat

Java Underground
Java Underground
169 участник(ов)

https://vk.com/javatutorial

Javanese Questions
Javanese Questions
109 участник(ов)

Чат предназначен для обмена знаниями строго в формате в вопрос-ответ. Тема — Java, Kotlin и Android. Вопрос должен быть предварительно прогуглен, понятно и грамотно сформулирован, помечен хэштегами. Ответ — тем более. Куски кода размером в несколько строк можно писать прямо здесь, для больших кусков кода стоит использовать http://gist.github.com/, http://pastebin.com/, https://codeshare.io/ или любой аналогичный сервис. В некоторых случаях можно прикреплять скриншоты. Стикеры и гифки запрещены. Дополнять и уточнять вопросы и ответы — редактированием исходного сообщения. Обсуждения должны приводить к редактированию вопроса/ответа и удаляться. По хештегам можно искать существующие вопросы и овтеты: #вопрос #ответ #git #generics #java #server #awt #javafx #swing #kotlin #anko #tornadofx #ktor #android #recyclerView #performance #arch #network #permissions #storage #async