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

Почему синхронизированный блок лучше, чем синхронизированный метод?

Я начал изучать синхронизацию в потоках.

Синхронизированный метод:

public class Counter {

   private static int count = 0;

   public static synchronized int getCount() {
      return count;
   }

   public synchronized setCount(int count) {
      this.count = count;
   }

}

Синхронизированный блок:

public class Singleton {

   private static volatile Singleton _instance;

   public static Singleton getInstance() {
      if (_instance == null) {
         synchronized(Singleton.class) {
            if (_instance == null)
               _instance = new Singleton();
         }
      }
      return _instance;
   }
}

Когда мне следует использовать метод synchronized и блок synchronized?

Почему блок synchronized лучше, чем метод synchronized?

4b9b3361

Ответ 1

Это не вопрос лучшего, просто другого.

Когда вы синхронизируете метод, вы эффективно синхронизируетесь с самим объектом. В случае статического метода вы выполняете синхронизацию с классом объекта. Таким образом, следующие две части кода выполняются одинаково:

public synchronized int getCount() {
    // ...
}

Это так, как вы написали это.

public int getCount() {
    synchronized (this) {
        // ...
    }
}

Если вы хотите контролировать синхронизацию с определенным объектом или хотите, чтобы часть метода была синхронизирована с объектом, укажите блок synchronized. Если вы используете ключевое слово synchronized в объявлении метода, он будет синхронизировать весь метод с объектом или классом.

Ответ 2

Хотя обычно это не проблема, с точки зрения безопасности лучше использовать синхронизацию на частном объекте, а не помещать его в метод.

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

Если вы используете блокировку частного элемента данных, вы можете предотвратить это, так как злоумышленник не может получить блокировку вашего частного объекта.

private final Object lockObject = new Object();

public void getCount() {
    synchronized( lockObject ) {
        ...
    }
}

Этот метод упоминается в блохе Effective Java (2-е изд.), Item # 70

Ответ 3

Разница в том, какая блокировка приобретается:

  • синхронизированный метод получает блокировку на весь объект. Это означает, что никакой другой поток не может использовать какой-либо синхронизированный метод во всем объекте, пока метод выполняется одним потоком.

  • Синхронизированные блоки получают блокировку объекта между круглыми скобками после ключевого слова synchronized. Это означает, что никакой другой поток не может получить блокировку заблокированного объекта до тех пор, пока не завершится синхронизированный блок.

Поэтому, если вы хотите заблокировать весь объект, используйте синхронизированный метод. Если вы хотите, чтобы другие части объекта были доступны другим потокам, используйте синхронизированный блок.

Если вы тщательно выберете заблокированный объект, синхронизированные блоки приведут к меньшему количеству конфликтов, поскольку весь объект/класс не заблокирован.

Это применимо аналогично статическим методам: синхронизированный статический метод получит блокировку всего объекта класса, в то время как синхронизированный блок внутри статического метода получит блокировку объекта между скобками.

Ответ 4

Определите "лучше". Синхронизированный блок только лучше, потому что он позволяет:

  1. Синхронизировать с другим объектом
  2. Ограничьте область синхронизации

Теперь ваш конкретный пример является примером шаблона двойной проверки блокировки, который является подозрительным (в более старых версиях Java он был сломан, и это легко сделать неправильно).

Если ваша инициализация дешевая, возможно, лучше инициализировать сразу с последним полем, а не по первому запросу, это также устранит необходимость синхронизации.

Ответ 5

Отличие между синхронизированным блоком и синхронизированным методом:

  • синхронизированный блок уменьшает объем блокировки, , но синхронизированный метод scope блокировки является целым методом.
  • синхронизированный блок имеет лучшую производительность, поскольку только критический раздел заблокирован , но синхронизированный метод имеет низкую производительность, чем блок.
  • синхронизированный блок обеспечивает гранулированный контроль блокировки блокировки блокировки , но блокируется либо на текущем объекте, представленном этим, либо на блокировке уровня класса.
  • синхронизированный блок может вызывать NullPointerException , но синхронизированный метод не бросает.
  • синхронизированный блок: synchronized(this){}

    синхронизированный метод: public synchronized void fun(){}

Ответ 6

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

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

public class AClass {
private int x;
private final Object lock = new Object();     //it must be final!

 public void setX() {
    synchronized(lock) {
        x++;
    }
 }
}

Ответ 7

В вашем случае оба эквивалентны!

Синхронизация статического метода эквивалентна синхронизированному блоку в соответствующем объекте класса.

Фактически, когда вы объявляете синхронизированный статический метод, блокировка получается на мониторе, соответствующем объекту Class.

public static synchronized int getCount() {
    // ...
}

совпадает с

public int getCount() {
    synchronized (ClassName.class) {
        // ...
    }
}

Ответ 8

Поскольку блокировка стоит дорого, когда вы используете синхронизированный блок, вы блокируете только, если _instance == null, а после _instance, наконец, инициализируется, вы никогда не заблокируете. Но при синхронизации по методу вы блокируете безоговорочно, даже после инициализации _instance. Это идея шаблона оптимизации двойной проверки http://en.wikipedia.org/wiki/Double-checked_locking.

Ответ 9

Это не следует рассматривать как вопрос наилучшего для использования, но это действительно зависит от варианта использования или сценария.

Синхронизированные методы

Весь метод может быть помечен как синхронизированный, что приводит к неявной блокировке этой ссылки (методы экземпляра) или класса (статические методы). Это очень удобный механизм для синхронизации.

Действия Поток обращается к синхронизированному методу. Он неявно получает блокировку и выполняет код. Если другой поток хочет получить доступ к указанному выше методу, он должен ждать. Поток не может получить блокировку, будет заблокирован и должен ждать, пока блокировка не будет отпущена.

Синхронизированные блоки

Чтобы получить блокировку объекта для определенного набора блоков кода, синхронизированные блоки лучше всего подходят. Поскольку блока достаточно, использование синхронизированного метода будет пустой тратой.

Более конкретно, с Синхронизированным блоком, можно определить ссылку на объект, на которой хотите получить блокировку.