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

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

Ответы [ 23 ]

1 голос
/ 16 августа 2017

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


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

  • Модификатор synchronized, указанный в сигнатуре метода
    1. виден в сгенерированном JavaDoc,
    2. определяется программно через отражение при тестировании модификатора метода для Модификатор. SYNCHRONIZED ,
    3. требует меньше текста и отступа по сравнению с synchronized(this) { .... } и
    4. (в зависимости от вашей IDE) отображается в схеме класса и дополнении кода,
    5. использует объект this в качестве блокировки при объявлении нестатического метода или включающего класса при объявлении статического метода.
  • Выражение 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
        }
    }
}
1 голос
/ 04 февраля 2019

Полагаю, этот вопрос касается разницы между Thread Safe Singleton и Ленивая инициализация с двойной проверкой блокировки . Я всегда ссылаюсь на эту статью, когда мне нужно реализовать какой-то конкретный синглтон.

Ну, это Thread Safe Singleton :

// Java program to create Thread Safe 
// Singleton class 
public class GFG  
{ 
  // private instance, so that it can be 
  // accessed by only by getInstance() method 
  private static GFG instance; 

  private GFG()  
  { 
    // private constructor 
  } 

 //synchronized method to control simultaneous access 
  synchronized public static GFG getInstance()  
  { 
    if (instance == null)  
    { 
      // if instance is null, initialize 
      instance = new GFG(); 
    } 
    return instance; 
  } 
} 

Плюсы:

  1. Возможна отложенная инициализация.

  2. Потокобезопасен.

Минусы:

  1. Метод getInstance () синхронизирован, поэтому он снижает производительность, так как несколько потоков не могут получить к нему доступ одновременно.

Это Ленивая инициализация с двойной проверкой блокировки :

// Java code to explain double check locking 
public class GFG  
{ 
  // private instance, so that it can be 
  // accessed by only by getInstance() method 
  private static GFG instance; 

  private GFG()  
  { 
    // private constructor 
  } 

  public static GFG getInstance() 
  { 
    if (instance == null)  
    { 
      //synchronized block to remove overhead 
      synchronized (GFG.class) 
      { 
        if(instance==null) 
        { 
          // if instance is null, initialize 
          instance = new GFG(); 
        } 

      } 
    } 
    return instance; 
  } 
} 

Плюсы:

  1. Возможна отложенная инициализация.

  2. Это также потокобезопасный.

  3. Производительность снижена из-за преодоления синхронизированного ключевого слова.

Минусы:

  1. В первый раз это может повлиять на производительность.

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

Пожалуйста, обратитесь к этой статье для более подробной информации:

https://www.geeksforgeeks.org/java-singleton-design-pattern-practices-examples/

0 голосов
/ 13 апреля 2013

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

Следующий код показывает эти пункты. Он также содержит потокобезопасный вектор, который будет заменен на ArrayList, чтобы показать, что многие потоки, добавляемые в вектор, не теряют никакой информации, в то время как то же самое с ArrayList может потерять информацию. 0) Текущий код показывает потерю информации из-за условий гонки A) Прокомментируйте текущую помеченную строку A, и раскомментируйте строку A над ней, затем запустите, метод теряет данные, но не должен. B) Обратный шаг A, раскомментируйте B и // конец блока}. Затем запустите, чтобы увидеть результаты без потери данных 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<Integer> raceList = new ArrayList<Integer>(); // simple add(#)
//  private Vector<Integer> raceList = new Vector<Integer>(); // 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<Thread> arT = new ArrayList<Thread>();

    // Create thread objects, add them to an array list
    for( int i=0; i<NUM_THREADS; i++){
        Thread rt = new RaceThread( ); // i );
        arT.add( rt );
    }

    // Start all object at once.
    for( Thread rt : arT ){
        rt.start();
    }

    // Wait for all threads to finish before we can print totals created by threads
    for( int i=0; i<NUM_THREADS; i++){
        try { arT.get(i).join(); }
        catch( InterruptedException ie ) { System.out.println("Interrupted thread "+i); }
    }

    // All threads finished, print the summary information.
    // (Try to print this informaiton without the join loop above)
    System.out.printf("\nRace condition, should have %,d. Really have %,d in array, and count of %,d.\n",
                MAX*NUM_THREADS, raceList.size(), raceCount );
    System.out.printf("Array lost %,d. Count lost %,d\n",
             MAX*NUM_THREADS-raceList.size(), MAX*NUM_THREADS-raceCount );
    }   // end RaceCondition constructor



    class RaceThread extends Thread {
    public void run() {
        for ( int i=0; i<MAX; i++){
        try {
            update( i );        
        }    // These  catches show when one thread steps on another's values
        catch( ArrayIndexOutOfBoundsException ai ){ System.out.print("A"); }
        catch( OutOfMemoryError oome ) { System.out.print("O"); }
        }
    }

    // so we don't lose counts, need to synchronize on some object, not primitive
    // Created "countLock" to show how this can work.
    // Comment out the synchronized and ending {, see that we lose counts.

//    public synchronized void update(int i){   // use A
    public void update(int i){                  // remove this when adding A
//      synchronized(countLock){            // or B
//      synchronized(this){             // or C
        raceCount = raceCount + 1;
        raceList.add( i );      // use Vector  
//          }           // end block for B or C
    }   // end update

    }   // end RaceThread inner class


} // end RaceCondition outter class
...