Модификатор synchronized
- это действительно плохая идея, которую следует избегать любой ценой. Я думаю, что похвально, что Sun пыталась сделать блокировку немного легче, но synchronized
просто доставляет больше хлопот, чем стоит.
Проблема в том, что метод synchronized
на самом деле является просто синтаксическим сахаром для получения блокировки на this
и ее удержания на протяжении всего метода. Таким образом, public synchronized void setInstanceVar()
будет эквивалентно примерно так:
public void setInstanceVar() {
synchronized(this) {
instanceVar++;
}
}
Это плохо по двум причинам:
- Все
synchronized
методы в одном классе используют одинаковую блокировку, что снижает пропускную способность
- Доступ к замку может получить любой, включая членов других классов.
Ничто не мешает мне делать что-то подобное в другом классе:
MyClass c = new MyClass();
synchronized(c) {
...
}
В этом блоке synchronized
я держу блокировку, которая требуется для всех методов synchronized
в MyClass
. Это дополнительно снижает пропускную способность и резко увеличивает шансы на тупик.
Лучше всего иметь выделенный объект lock
и напрямую использовать блок synchronized(...)
:
public class MyClass {
private int instanceVar;
private final Object lock = new Object(); // must be final!
public void setInstanceVar() {
synchronized(lock) {
instanceVar++;
}
}
}
В качестве альтернативы вы можете использовать интерфейс java.util.concurrent.Lock
и реализацию java.util.concurrent.locks.ReentrantLock
для достижения в основном одного и того же результата (фактически, он одинаков в Java 6).