В чем разница между синхронизацией на lockObject и использованием этого как блокировки?
Я знаю разницу между синхронизированным методом и синхронизированным блоком, но я не уверен в части синхронизации.
Предполагая, что у меня есть этот код
class Test {
private int x=0;
private Object lockObject = new Object();
public void incBlock() {
synchronized(lockObject) {
x++;
}
System.out.println("x="+x);
}
public void incThis() { // same as synchronized method
synchronized(this) {
x++;
}
System.out.println("x="+x);
}
}
В этом случае в чем разница между использованием lockObject и использованием этого как блокировки? Кажется, что то же самое для меня.
Когда вы решите использовать синхронизированный блок, как вы решаете, какой из объектов является блокировкой?
Ответы
Ответ 1
Лично я почти никогда не блокирую "this". Обычно я блокирую конфиденциальную ссылку, которую знаю, что никакой другой код не будет заблокирован. Если вы заблокируете "this", тогда любой другой код, который знает о вашем объекте, может захотеть заблокировать его. Хотя это вряд ли произойдет, это, безусловно, может сделать - и может вызвать взаимоблокировки или просто чрезмерную блокировку.
Нет ничего особенного в том, что вы запираете - вы можете думать об этом как о знаке, эффективно. Любая блокировка с тем же токеном будет пытаться получить тот же замок. Если вы не хотите, чтобы другой код мог получить один и тот же замок, используйте приватную переменную. Я бы также рекомендовал вам сделать переменную final
- я не могу вспомнить ситуацию, когда я когда-либо хотел изменить переменную блокировки на время жизни объекта.
Ответ 2
У меня был такой же вопрос, когда я читал Java Concurrency In Practice, и я подумал, что добавлю дополнительную точку зрения на ответы, предоставленные Jon Skeet и spullara.
Вот пример кода, который будет блокировать даже "быстрые" методы setValue(int)
/getValue()
, пока выполняется doStuff(ValueHolder)
.
public class ValueHolder {
private int value = 0;
public synchronized void setValue(int v) {
// Or could use a sychronized(this) block...
this.value = 0;
}
public synchronized int getValue() {
return this.value;
}
}
public class MaliciousClass {
public void doStuff(ValueHolder holder) {
synchronized(holder) {
// Do something "expensive" so setter/getter calls are blocked
}
}
}
Недостатком использования this
для синхронизации является то, что другие классы могут синхронизироваться по ссылке на ваш класс (не через this
, конечно). Вредоносный или непреднамеренное использование ключевого слова synchronized
при блокировке ссылки на объект может привести к тому, что ваш класс будет плохо себя вести при параллельном использовании, поскольку внешний класс может эффективно блокировать ваши this
-синхронизированные методы, и вы ничего не можете сделать (в ваш класс), чтобы запретить это во время выполнения. Чтобы избежать этой потенциальной ошибки, вы должны синхронизировать с private final Object
или использовать интерфейс Lock
в java.util.concurrent.locks
.
Для этого простого примера вы можете поочередно использовать AtomicInteger
, а не синхронизировать setter/getter.
Ответ 3
Пункт 67 Эффективное второе издание Java - Избегайте чрезмерной синхронизации, поэтому я бы синхронизировал с закрытым объектом блокировки.
Ответ 4
Каждый объект в Java может выступать в качестве монитора. Выбор одного зависит от того, какую гранулярность вы хотите. Выбор 'this' имеет преимущество и недостаток, что другие классы могут также синхронизироваться на одном мониторе. Мой совет заключается в том, чтобы избежать использования ключевого слова synchronize напрямую и вместо этого использовать конструкции из библиотеки java.util.concurrency, которые являются более высоким уровнем и имеют четко определенную семантику. У этой книги есть много замечательных советов в ней от очень известных экспертов:
Java Concurrency на практике
http://amzn.com/0321349601
Ответ 5
В этом случае не имеет значения, какой объект вы выберете для блокировки. Но вы должны последовательно использовать один и тот же объект для блокировки для достижения правильной синхронизации. Вышеуказанный код не обеспечивает правильную синхронизацию, поскольку вы когда-то использовали объект 'this' как блокировку, а затем "lockObject" как блокировку.