Краткий ответ: вам не нужна синхронизация. Оба подхода эквивалентны с точки зрения безопасности потока.
Более длинный ответ:
Возможно, стоит сделать шаг назад и вспомнить, что делает синхронизированный блок. По сути, это две вещи:
- гарантирует, что если поток A находится внутри блока, который синхронизирован с объектом M, никакой другой поток не может войти в блок, синхронизированный с тем же объектом M, пока поток A не завершит работу со своим блоком кода
- проверяет, что если поток A выполнил работу в блоке, который синхронизирует объект M, а затем завершает этот блок, а затем поток B входит в блок, который также синхронизируется
M, тогда поток B увидит все, что поток A сделал в своем синхронизированном блоке. Это называется установлением отношения «происходит до того».
Обратите внимание, что синхронизированный метод - это просто сокращение для переноса кода метода в синхронизированный (this) {...}.
В дополнение к этим двум вещам модель памяти Java (JMM) гарантирует, что в одном потоке все будет происходить так, как если бы они не были переупорядочены. (Они могут быть переупорядочены по разным причинам, включая эффективность - но не так, как ваша программа может заметить в одном потоке. Например, если вы выполните «x = 1; y = 2», компилятор может свободно переключаться что такое, что y = 2 происходит до x = 1, так как один поток не может заметить разницу. Если несколько потоков обращаются к x и y, то очень возможно, без надлежащей синхронизации, для другого потока увидеть y = 2 прежде чем он увидит х = 1.)
Итак, возвращаясь к вашему первоначальному вопросу, есть пара интересных заметок.
Во-первых, поскольку синхронизированный метод является сокращением для помещения всего метода в блок "synchronized (this) {...}", методы t1 и методы t2 не будут синхронизироваться с одной и той же ссылкой и, следовательно, не будут синхронизированы относительно друг друга. Методы t1 будут синхронизироваться только с объектом t1, а методы t2 будут синхронизироваться только с t2. Другими словами, было бы прекрасно для одновременной работы t1.method1 () и t2.method1 (). Итак, из этих двух вещей, которые предоставляет ключевое слово synchronized, первое (исключительность входа в блок) не имеет значения. Все может пойти примерно так:
- t1 хочет ввести метод1. Он должен получить монитор t1, который не оспаривается, поэтому он получает его и входит в блок
- t2. хочет ввести метод2. Ему нужно получить 11 мониторов, который не оспаривается - он получает его и входит в блок
- t1 завершает method1 и освобождает удержание на мониторе t1
- t2 завершает method1 и отпускает удержание на мониторе t2
Что касается второй вещи, которую выполняет синхронизация (установление происходит до), синхронизация method1 () и method2 () будет в основном гарантировать, что t1.method1 () произойдет до t1.method2 (). Но поскольку оба эти события в любом случае происходят в одном потоке (поток t1), JMM в любом случае гарантирует, что это произойдет.
Так что на самом деле все становится еще немного ужаснее. Если бы t1 и t2 имели общее состояние - то есть, была бы необходима синхронизация - тогда было бы достаточно сделать синхронизированные методы , а не . Помните, что синхронизированный метод означает синхронизированный (this) {...}, поэтому методы t1 будут синхронизированы с t1, а методы t2 будут с t2. На самом деле вы бы не установили никаких отношений «происходит до» между методами t1 и t2.
Вместо этого вам нужно убедиться, что методы синхронизированы по одной и той же ссылке . Есть разные способы сделать это, но в основном это должна быть ссылка на объект, о котором оба потока знают.
Предположим, что t1 и t2 оба знают об одной и той же ссылке, LOCK. Оба имеют такие методы, как:
method1() {
synchronized(LOCK) {
// do whatever
}
}
Теперь все может пойти примерно так:
- t1 хочет ввести метод1. Он должен получить монитор LOCK, который не оспаривается, поэтому он получает его и входит в блок
- t2 хочет ввести метод1. Он должен получить монитор LOCK, который уже удерживается t1, поэтому t2 удерживается.
- t1 завершает method1 и освобождает удержание на мониторе LOCK
- t2 теперь может получить монитор LOCK, поэтому он делает это и запускается по методу метода 1
- t2 завершает method1 и отпускает удержание на мониторе LOCK