В чем разница между синхронизацией на lockObject и использованием этого в качестве блокировки? - PullRequest
46 голосов
/ 30 июля 2010

Я знаю разницу между синхронизированным методом и синхронизированным блоком, но я не уверен насчет части синхронизированного блока.

Если у меня есть этот код

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 и использованием this в качестве блокировки? Мне кажется, то же самое ..

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

Ответы [ 5 ]

63 голосов
/ 30 июля 2010

Лично я почти никогда не блокируюсь на "этом". Я обычно блокирую частную ссылку, которую я знаю, что никакой другой код не будет блокироваться. Если вы заблокируете «this», тогда любой другой код, который знает о вашем объекте, может решить заблокировать его. Хотя это вряд ли произойдет, это, безусловно, может произойти - и может вызвать взаимные блокировки или просто чрезмерную блокировку.

Нет ничего особенно волшебного в том, что вы фиксируете - вы можете эффективно воспринимать это как знак. Любой блокирующий с тем же маркером будет пытаться получить тот же самый замок. Если вы не хотите, чтобы другой код мог получить такую ​​же блокировку, используйте закрытую переменную. Я бы также рекомендовал бы вам создать переменную final - я не могу вспомнить ситуацию, когда я когда-либо хотел изменить переменную блокировки в течение времени жизни объекта .

11 голосов
/ 02 августа 2010

У меня возник тот же вопрос, когда я читал «Параллелизм на практике на Java», и я подумал, что хотел бы добавить дополнительную точку зрения на ответы, представленные Джоном Скитом и Спулларой.

Вот пример кода, который блокирует даже «быстрые» методы 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 вместо синхронизации с установщиком / получателем.

5 голосов
/ 30 июля 2010

Пункт 67 из Effective Java Second Edition - Избегайте чрезмерной синхронизации, поэтому я бы синхронизировал объект с частной блокировкой.

1 голос
/ 30 июля 2010

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

Параллелизм Java на практике http://amzn.com/0321349601

0 голосов
/ 30 июля 2010

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

...