Есть ли преимущество использования Синхронизированного метода вместо Синхронизированного блока?

Может ли кто-нибудь сказать мне преимущество синхронизированного метода по синхронизированному блоку с примером?

Может ли кто-нибудь сказать мне преимущество синхронизированного метода над синхронизированным блоком с примером? Благодарю.

Не существует явного преимущества использования синхронизируемого метода над блоком.

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

Метод:

 public synchronized void method() { // blocks "this" from here.... ... ... ... } // to here 

Блок:

 public void method() { synchronized( this ) { // blocks "this" from here .... .... .... .... } // to here... } 

Видеть? Никакого преимущества.

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

Для сравнения:

 // locks the whole object ... private synchronized void someInputRelatedWork() { ... } private synchronized void someOutputRelatedWork() { ... } 

против

 // Using specific locks Object inputLock = new Object(); Object outputLock = new Object(); private void someInputRelatedWork() { synchronized(inputLock) { ... } } private void someOutputRelatedWork() { synchronized(outputLock) { ... } } 

Также, если метод растет, вы все равно можете синхронизировать секцию:

  private void method() { ... code here ... code here ... code here synchronized( lock ) { ... very few lines of code here } ... code here ... code here ... code here ... code here } 

Единственное реальное отличие состоит в том, что синхронизированный блок может выбирать, на какой объект он синхронизируется. Синхронизированный метод может использовать только 'this' (или соответствующий экземпляр classа для метода синхронизированного classа). Например, они семантически эквивалентны:

 synchronized void foo() { ... } void foo() { synchronized (this) { ... } } 

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

Синхронный метод

Плюсы:

  • В вашей среде IDE можно указать синхронизированные методы.
  • Синтаксис более компактен.
  • Сила разделения разделенных блоков на отдельные методы.

Минусы:

  • Синхронизируется с этим, поэтому позволяет посторонним также синхронизировать с ним.
  • Сложнее переместить код за пределы синхронизированного блока.

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

Плюсы:

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

Минусы:

  • Синтаксис более сложный, поэтому код становится сложнее читать.

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

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

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

 Object writeLock = new Object(); 

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

 synchronized(writeLock){ // do something } 

Таким образом, потребители могут читать, и производители будут заблокированы.

Синхронный метод

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

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

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

Синхронизированное заявление

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

Q: Внутренние блокировки и синхронизация Синхронизация построена вокруг внутреннего объекта, известного как встроенная блокировка или блокировка монитора. (Спецификация API часто относится к этому объекту просто как к «монитору».) Внутренние блокировки играют роль в обоих аспектах синхронизации: принудительный эксклюзивный доступ к состоянию объекта и установление происходит до отношений, которые необходимы для видимости.

Каждый объект имеет встроенный замок. По соглашению stream, который нуждается в эксклюзивном и последовательном доступе к полям объекта, должен получить внутреннюю блокировку объекта перед тем, как получить доступ к ним, а затем освободить внутреннюю блокировку, когда это будет сделано с ними. Говорят, что stream имеет собственный замок между временем, когда он приобрел замок, и освободил замок. Пока stream владеет встроенной блокировкой, ни один другой stream не может получить одну и ту же блокировку. Другой stream будет блокироваться, когда он попытается получить блокировку.

 package test; public class SynchTest implements Runnable { private int c = 0; public static void main(String[] args) { new SynchTest().test(); } public void test() { // Create the object with the run() method Runnable runnable = new SynchTest(); Runnable runnable2 = new SynchTest(); // Create the thread supplying it with the runnable object Thread thread = new Thread(runnable,"thread-1"); Thread thread2 = new Thread(runnable,"thread-2"); // Here the key point is passing same object, if you pass runnable2 for thread2, // then its not applicable for synchronization test and that wont give expected // output Synchronization method means "it is not possible for two invocations // of synchronized methods on the same object to interleave" // Start the thread thread.start(); thread2.start(); } public synchronized void increment() { System.out.println("Begin thread " + Thread.currentThread().getName()); System.out.println(this.hashCode() + "Value of C = " + c); // If we uncomment this for synchronized block, then the result would be different // synchronized(this) { for (int i = 0; i < 9999999; i++) { c += i; } // } System.out.println("End thread " + Thread.currentThread().getName()); } // public synchronized void decrement() { // System.out.println("Decrement " + Thread.currentThread().getName()); // } public int value() { return c; } @Override public void run() { this.increment(); } } 

Перекрестно проверять разные выходы с синхронизированным методом, блокировать и без синхронизации.

Примечание. Статические синхронизированные методы и блоки работают над объектом Class.

 public class MyClass { // locks MyClass.class public static synchronized void foo() { // do something } // similar public static void foo() { synchronized(MyClass.class) { // do something } } } 

Когда java-компилятор преобразует исходный код в байтовый код, он обрабатывает синхронизированные методы и синхронизированные блоки по-разному.

Когда JVM выполняет синхронизированный метод, исполняемый stream идентифицирует, что в структуре метода method_info метода установлен флаг ACC_SYNCHRONIZED, он автоматически получает блокировку объекта, вызывает метод и освобождает блокировку. Если возникает исключение, stream автоматически освобождает блокировку.

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

Это показывает вызовы для генерации как синхронизированного метода, так и синхронизированного блока:

 public class SynchronizationExample { private int i; public synchronized int synchronizedMethodGet() { return i; } public int synchronizedBlockGet() { synchronized( this ) { return i; } } } 

Метод synchronizedMethodGet() генерирует следующий байтовый код:

 0: aload_0 1: getfield 2: nop 3: iconst_m1 4: ireturn 

И вот код байта из метода synchronizedBlockGet() :

 0: aload_0 1: dup 2: astore_1 3: monitorenter 4: aload_0 5: getfield 6: nop 7: iconst_m1 8: aload_1 9: monitorexit 10: ireturn 11: astore_2 12: aload_1 13: monitorexit 14: aload_2 15: athrow 

Одно существенное различие между синхронизированным методом и блоком состоит в том, что Синхронизированный блок обычно уменьшает объем блокировки. Поскольку объем блокировки обратно пропорционален производительности, всегда лучше блокировать только критический раздел кода. Одним из лучших примеров использования синхронизированного блока является двойная проверка блокировки в шаблоне Singleton, где вместо блокирования всего метода getInstance() мы блокируем только критический раздел кода, который используется для создания экземпляра Singleton. Это значительно улучшает производительность, поскольку блокировка требуется только один или два раза.

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

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

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

 private List myList = new ArrayList(); private Map(); public void put( String s, Bar b ) { synchronized( myMap ) { myMap.put( s,b ); // then some thing that may take a while like a database access or RPC or notifying listeners } } public void hasKey( String s, ) { synchronized( myMap ) { myMap.hasKey( s ); } } public void add( Foo f ) { synchronized( myList ) { myList.add( f ); // then some thing that may take a while like a database access or RPC or notifying listeners } } public Thing getMedianFoo() { Foo med = null; synchronized( myList ) { Collections.sort(myList); med = myList.get(myList.size()/2); } return med; } 

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

Синхронизированные методы можно проверить с помощью API отражения. Это может быть полезно для тестирования некоторых контрактов, например, все методы в модели синхронизированы .

Следующий fragment печатает все синхронизированные методы Hashtable:

 for (Method m : Hashtable.class.getMethods()) { if (Modifier.isSynchronized(m.getModifiers())) { System.out.println(m); } } 

Важное примечание по использованию синхронизированного блока: осторожно, что вы используете в качестве объекта блокировки!

Фрагмент кода из user2277816 выше иллюстрирует эту точку в том, что ссылка на строковый литерал используется в качестве объекта блокировки. Поймите, что строковые литералы автоматически интернированы на Java, и вы должны начать понимать проблему: каждая часть кода, которая синхронизируется с литеральной «блокировкой», имеет одинаковую блокировку! Это может легко привести к взаимоблокировкам с совершенно несвязанными fragmentами кода.

Это не просто объекты String, с которыми вам нужно быть осторожными. Вмешаемые примитивы также представляют опасность, так как autoboxing и методы valueOf могут повторно использовать одни и те же объекты в зависимости от значения.

Для получения дополнительной информации см. https://www.securecoding.cert.org/confluence/display/java/LCK01-J.+Do+not+synchronize+on+objects+that+may+be+reused

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

Вот пример

Уровень метода

 class MethodLevel { //shared among threads SharedResource x, y ; public void synchronized method1() { //multiple threads can't access } public void synchronized method2() { //multiple threads can't access } public void method3() { //not synchronized //multiple threads can access } } 

Уровень блока

 class BlockLevel { //shared among threads SharedResource x, y ; //dummy objects for locking Object xLock = new Object(); Object yLock = new Object(); public void method1() { synchronized(xLock){ //access x here. thread safe } //do something here but don't use SharedResource x, y // because will not be thread-safe synchronized(xLock) { synchronized(yLock) { //access x,y here. thread safe } } //do something here but don't use SharedResource x, y //because will not be thread-safe }//end of method1 } 

[Редактировать]

Для Collection такой как Vector и Hashtable они синхронизируются, когда ArrayList или HashMap отсутствуют, и вам нужно установить синхронизированное ключевое слово или вызвать метод синхронизации, выбранный Collections:

 Map myMap = Collections.synchronizedMap (myMap); // single lock for the entire map List myList = Collections.synchronizedList (myList); // single lock for the entire list 

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

Единственное отличие: синхронизированные блоки позволяют выполнять гранулированную блокировку в отличие от синхронизированного метода

В основном synchronized блок или методы были использованы для написания кода безопасности streamа, избегая ошибок несогласованности памяти.

Этот вопрос очень старый, и многое изменилось за последние 7 лет. Для обеспечения безопасности streamов были введены новые конструкции программирования.

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

Блокировка объектов поддерживает блокировки идиом, которые упрощают многие параллельные приложения.

Исполнители определяют API высокого уровня для запуска и управления streamами. Реализации Executor, предоставленные java.util.concurrent, обеспечивают управление пулами streamов, подходящее для широкомасштабных приложений.

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

Атомные переменные имеют функции, которые минимизируют синхронизацию и помогают избежать ошибок согласованности памяти.

ThreadLocalRandom (в JDK 7) обеспечивает эффективное генерирование псевдослучайных чисел из нескольких streamов.

Лучшей заменой для синхронизации является ReentrantLock , которая использует Lock API

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

Пример с замками:

 class X { private final ReentrantLock lock = new ReentrantLock(); // ... public void m() { lock.lock(); // block until condition holds try { // ... method body } finally { lock.unlock() } } } 

Обратитесь к java.util.concurrent и java.util.concurrent.atomic пакетам тоже для других программных конструкций.

См. Также этот связанный вопрос:

Синхронизация против блокировки

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

Как уже говорилось, синхронизированный блок может использовать определяемую пользователем переменную как объект блокировки, когда синхронизированная функция использует только «это». И, конечно, вы можете манипулировать областями вашей функции, которые должны быть синхронизированы. Но каждый говорит, что нет никакой разницы между синхронизированной функцией и блоком, которая охватывает всю функцию, используя «это» как объект блокировки. Это неверно, разница в байтовом коде, который будет генерироваться в обеих ситуациях. В случае использования синхронизированного блока должна быть назначена локальная переменная, которая ссылается на «это». И в результате у нас будет немного больший размер для функции (не имеет значения, если у вас есть только несколько функций).

Более подробное объяснение разницы вы можете найти здесь: http://www.artima.com/insidejvm/ed2/threadsynchP.html

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

Пример :

  Class Example { String test = "abc"; // lock will be acquired on String test object. synchronized (test) { // do something } lock will be acquired on Example Object public synchronized void testMethod() { // do some thing } } 

Я знаю, что это старый вопрос, но с моим быстрым рассмотрением ответов здесь я действительно не видел, чтобы кто-нибудь упоминал, что иногда synchronized метод может быть неправильной блокировкой.
Из Java Concurrency In Practice (стр. 72):

 public class ListHelper { public List list = Collections.syncrhonizedList(new ArrayList<>()); ... public syncrhonized boolean putIfAbsent(E x) { boolean absent = !list.contains(x); if(absent) { list.add(x); } return absent; } 

Вышеприведенный код имеет вид streamобезопасности. Однако на самом деле это не так. В этом случае блокировка получается на экземпляре classа. Тем не менее, этот список может быть изменен другим streamом, не использующим этот метод. Правильный подход заключается в использовании

 public boolean putIfAbsent(E x) { synchronized(list) { boolean absent = !list.contains(x); if(absent) { list.add(x); } return absent; } } 

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

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

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

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

Из краткого описания спецификации Java: http://www.cs.cornell.edu/andru/javaspec/17.doc.html

Синхронизированный оператор (§14.17) вычисляет ссылку на объект; он затем пытается выполнить действие блокировки для этого объекта и не продвигается дальше, пока действие блокировки не будет успешно завершено. …

Синхронизированный метод (§8.4.3.5) автоматически выполняет действие блокировки при его вызове; его тело не выполняется до тех пор, пока действие блокировки не будет успешно завершено. Если метод является методом экземпляра , он блокирует блокировку, связанную с экземпляром, для которого он был вызван (то есть, объект, который будет известен как это во время выполнения тела метода). Если этот метод является статическим , он блокирует блокировку, связанную с объектом classа, который представляет class, в котором определяется метод. …

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

Edit: Я изначально думал, что это кавычки фактической спецификации Java. Уточнено, что эта страница является просто сводкой / объяснением спецификации

TLDR; Не используйте synchronized модификатор или synchronized(this){...} выражение synchronized(this){...} но synchronized(myLock){...} где myLock – это конечное поле экземпляра, в котором находится частный объект.


Разница между использованием synchronized модификатора в объявлении метода и выражением synchronized(..){ } в теле метода:

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

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

Поэтому наилучшей практикой является использование synchronized модификатора и synchronized(...) выражения в сочетании с this как объект блокировки, но закрытый объект блокировки для этого объекта. Например:

 public class MyService { private final lock = new Object(); public void doThis() { synchronized(lock) { // do code that requires synchronous execution } } public void doThat() { synchronized(lock) { // do code that requires synchronous execution } } } 

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

 public class MyService { private final lock1 = new Object(); private final lock2 = new Object(); public void doThis() { synchronized(lock1) { synchronized(lock2) { // code here is guaranteed not to be executes at the same time // as the synchronized code in doThat() and doMore(). } } public void doThat() { synchronized(lock1) { // code here is guaranteed not to be executes at the same time // as the synchronized code in doThis(). // doMore() may execute concurrently } } public void doMore() { synchronized(lock2) { // code here is guaranteed not to be executes at the same time // as the synchronized code in doThis(). // doThat() may execute concurrently } } } 

Синхронизация с streamами. 1) НИКОГДА не используйте синхронизированный (это) в streamе, он не работает. Синхронизация с (этим) использует текущий stream как объект блокирующего streamа. Поскольку каждый stream не зависит от других streamов, координация синхронизации отсутствует. 2) Тесты кода показывают, что в Java 1.6 на Mac синхронизация метода не работает. 3) synchronized (lockObj), где lockObj – общий общий объект всех streamов, синхронизирующих на нем, будет работать. 4) Работа ReenterantLock.lock () и .unlock (). См. Учебники по Java для этого.

Следующий код показывает эти моменты. Он также содержит streamобезопасный вектор, который будет заменен ArrayList, чтобы показать, что многие streamи, добавляющие в вектор, не теряют никакой информации, в то время как то же самое с ArrayList может потерять информацию. 0) Текущий код показывает потерю информации из-за условий гонки. A) Прокомментируйте текущую помеченную строку A и раскомментируйте линию A над ней, затем запустите, метод потеряет данные, но это не должно. B) Обратный шаг A, uncomment B и // end block}. Затем запустите, чтобы увидеть результаты без потери данных. C) Прокомментировать B, раскомментировать C. Запустить, увидеть синхронизацию (это), как и ожидалось, теряет данные. Не успевайте завершить все варианты, надеюсь, что это поможет. Если синхронизация (эта) или синхронизация метода работает, укажите, какую версию Java и ОС вы протестировали. Спасибо.

 import java.util.*; /** RaceCondition - Shows that when multiple threads compete for resources thread one may grab the resource expecting to update a particular area but is removed from the CPU before finishing. Thread one still points to that resource. Then thread two grabs that resource and completes the update. Then thread one gets to complete the update, which over writes thread two's work. DEMO: 1) Run as is - see missing counts from race condition, Run severa times, values change 2) Uncomment "synchronized(countLock){ }" - see counts work Synchronized creates a lock on that block of code, no other threads can execute code within a block that another thread has a lock. 3) Comment ArrayList, unComment Vector - See no loss in collection Vectors work like ArrayList, but Vectors are "Thread Safe" May use this code as long as attribution to the author remains intact. /mf */ public class RaceCondition { private ArrayList raceList = new ArrayList(); // simple add(#) // private Vector raceList = new Vector(); // simple add(#) private String countLock="lock"; // Object use for locking the raceCount private int raceCount = 0; // simple add 1 to this counter private int MAX = 10000; // Do this 10,000 times private int NUM_THREADS = 100; // Create 100 threads public static void main(String [] args) { new RaceCondition(); } public RaceCondition() { ArrayList arT = new ArrayList(); // Create thread objects, add them to an array list for( int i=0; i 
Давайте будем гением компьютера.