Подтвердить что ты не робот

В чем разница между синхронизацией на 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 и использованием этого как блокировки? Кажется, что то же самое для меня.

Когда вы решите использовать синхронизированный блок, как вы решаете, какой из объектов является блокировкой?

4b9b3361

Ответ 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.

Ответ 4

Каждый объект в Java может выступать в качестве монитора. Выбор одного зависит от того, какую гранулярность вы хотите. Выбор 'this' имеет преимущество и недостаток, что другие классы могут также синхронизироваться на одном мониторе. Мой совет заключается в том, чтобы избежать использования ключевого слова synchronize напрямую и вместо этого использовать конструкции из библиотеки java.util.concurrency, которые являются более высоким уровнем и имеют четко определенную семантику. У этой книги есть много замечательных советов в ней от очень известных экспертов:

Java Concurrency на практике http://amzn.com/0321349601

Ответ 5

В этом случае не имеет значения, какой объект вы выберете для блокировки. Но вы должны последовательно использовать один и тот же объект для блокировки для достижения правильной синхронизации. Вышеуказанный код не обеспечивает правильную синхронизацию, поскольку вы когда-то использовали объект 'this' как блокировку, а затем "lockObject" как блокировку.