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

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

Ответы [ 23 ]

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

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

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

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

Метод:

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

Блок:

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

См? Никаких преимуществ вообще.

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

Сравнить:

// 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
}
138 голосов
/ 22 февраля 2009

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

synchronized void foo() {
  ...
}

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

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

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

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

Плюсы:

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

Минусы:

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

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

Плюсы:

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

Минусы:

  • Синтаксис более сложный, что затрудняет чтение кода.

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

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

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

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

Object writeLock = new Object();

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

synchronized(writeLock){
  // do something
}

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

30 голосов
/ 19 апреля 2011

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

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

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

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

Синхронизированный оператор

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

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

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

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

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

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

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

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

   // similar
   public static void foo() {
      synchronized(MyClass.class) {
// do something
      }
   }
}
17 голосов
/ 21 апреля 2014

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

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

Синхронизация блока метода, с другой стороны, обходит встроенную поддержку 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. Это резко повышает производительность, поскольку блокировка требуется только один или два раза.

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

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

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

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

private List<Foo> myList = new ArrayList<Foo>();
private Map<String,Bar) myMap = new HashMap<String,Bar>();

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;
}
7 голосов
/ 22 февраля 2009

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

6 голосов
/ 10 декабря 2012

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

В следующем фрагменте печатаются все синхронизированные методы Hashtable:

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