2014-01-03 5 views
34

Я начал изучать синхронизацию потоков.Почему синхронизированный блок лучше синхронизированного метода?

Синхронное метод:

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; 
} 

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

+1

Вы также должны прочитать http://en.wikipedia.org/wiki/Double-checked_locking –

+1

В в первом случае я бы использовал AtomicInteger, и во втором случае я использую 'enum' для Singleton. –

+0

Где он говорит, что лучше? В чем заключается ваш вопрос? – EJP

ответ

41

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

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

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

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

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

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

+0

. То же самое относится и к переменным класса и методам, за исключением того, что для соответствующего объекта класса получен экземпляр, а не экземпляр экземпляра. –

+5

Да, это по-другому, но есть очень веские причины, по которым вы никогда не хотите синхронизировать «this», поэтому этот ответ пропускает точку немного imo. – Voo

+3

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

2

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

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

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

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

такой же, как

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

ОК. Если я синхронизирую нестатический метод, то каков будет результат – UnderDog

+0

Тогда это экземпляр или создаваемый объект. Два потока могут обрабатывать один и тот же метод в двух разных объектах независимо друг от друга. –

4

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

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

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

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

2

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

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

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

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

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

23

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

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

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

private final Object lockObject = new Object(); 

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

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

+1

Даже не должен быть злым, очень просто и невинно искать объект, который вы получаете где-то, чтобы заблокировать (скажем, потому что вы хотите синхронизировать доступ именно к этому объекту). Лучше избегать таких проблем – Voo

+3

@wolfcastle: Не могли бы вы объяснить, что такое «злонамеренный пользователь вашего кода»? Как они вредны, испортив собственное приложение? –

+6

@ErickRobertson Image Вы предоставляете какой-то сервис с открытым API. Если ваш API предоставляет некоторый изменяемый объект, действие которого зависит от блокировки этого объекта, один злонамеренный клиент может получить объект, заблокировать его, а затем цикл навсегда, удерживая блокировку. Это может помешать другим клиентам работать правильно. Атака типа «отказ в обслуживании». Поэтому они не испортят свое приложение. – wolfcastle

15

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

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

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

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

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

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

+0

Итак, если у меня есть синхронизированный блок и поток запущен в нем, может ли другой поток войти в объект и запустить синхронизированный блок где-нибудь еще в объекте? Если у меня есть синхронизированный метод и один поток выполняется в нем, ни один другой поток не может выполнить в этом объекте или только в синхронизированных областях объекта? –

1

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

синхронных Методы

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

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

синхронных Блоки

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

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

0

Разница между синхронизируется блок и синхронизированы метод следующие:

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

    синхронизируется метод: public synchronized void fun(){}

Смежные вопросы