Есть ли преимущество использования синхронизированного метода вместо синхронизированного блока? - PullRequest
395 голосов
/ 22 февраля 2009

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

Ответы [ 23 ]

5 голосов
/ 21 мая 2016

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

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

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

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

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

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

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

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

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

Лучшей заменой для синхронизированной является 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 для других программных конструкций.

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

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

5 голосов
/ 30 сентября 2013

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

Вот пример

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

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
5 голосов
/ 12 августа 2013

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

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

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

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

4 голосов
/ 14 июля 2010

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

3 голосов
/ 22 февраля 2009

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

2 голосов
/ 08 декабря 2016

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

public class ListHelper<E> {
  public List<E> list = Collections.syncrhonizedList(new ArrayList<>());
...

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

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

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

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

2 голосов
/ 13 декабря 2015

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

Пример:

    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
   } 

   }
2 голосов
/ 28 октября 2015

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

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

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

2 голосов
/ 16 июля 2013

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

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

1 голос
/ 18 декабря 2013

Из резюме спецификации Java: http://www.cs.cornell.edu/andru/javaspec/17.doc.html

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

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

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

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

...