Цитаты из JLS (включая пример):
Оператор synchronized
получает блокировку взаимного исключения от имени исполняющего потока, выполняет блок, а затем снимает блокировку. Пока исполняющий поток владеет блокировкой, никакой другой поток не может получить блокировку.
Метод synchronized
получает монитор перед его выполнением. Для метода класса (static
) используется монитор, связанный с объектом Class
для класса метода. Для метода экземпляра используется монитор, связанный с this
(объект, для которого был вызван метод).
Это те же самые блокировки, которые могут использоваться оператором synchronized
; таким образом, код:
class Test {
int count;
synchronized void bump() { count++; }
static int classCount;
static synchronized void classBump() {
classCount++;
}
}
имеет точно такой же эффект, как:
class BumpTest {
int count;
void bump() {
synchronized (this) {
count++;
}
}
static int classCount;
static void classBump() {
try {
synchronized (Class.forName("BumpTest")) {
classCount++;
}
} catch (ClassNotFoundException e) {
...
}
}
}
Так чем они отличаются?
Цитата из Effective Java 2nd Edition , пункт 67: избегать чрезмерной синхронизации:
Как правило, вы должны выполнять как можно меньше работы в synchronized
регионах.
Модификатор synchronized
для методов, являющийся синтаксическим сахаром, является применимым во многих, но не во всех сценариях. В этой книге более подробно обсуждается, почему вы должны избегать чрезмерной синхронизации, но, в основном, используя операторы synchronized
, вы получаете гораздо больший контроль над границами ваших synchronized
регионов (и, если этого требует сценарий, вы также можете выберите свои собственные замки).
Если ваш метод не очень прост и / или вам не нужно получить блокировку this
на весь период действия метода (или блокировку объекта Class
, если метод static
), вы должны использовать synchronized
операторы, ограничивающие синхронизацию внутри метода только тем моментом, когда он вам нужен (т. е. когда вы обращаетесь к изменяемым данным общего доступа)