Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

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

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

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

Метод:

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
}

Ответ 2

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

synchronized void foo() {
  ...
}

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

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

Ответ 3

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

Плюсы:

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

Минусы:

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

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

Плюсы:

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

Минусы:

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

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

Ответ 4

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

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

Object writeLock = new Object();

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

synchronized(writeLock){
  // do something
}

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

Ответ 5

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

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

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

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

Синхронизированное утверждение

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

Q: Внутренние блокировки и синхронизация Синхронизация построена вокруг внутреннего объекта, известного как встроенная блокировка или блокировка монитора. (Спецификация 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();
    }
}

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

Ответ 6

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

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

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

Ответ 7

Когда 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. Это значительно улучшает производительность, поскольку блокировка требуется только один или два раза.

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

Ответ 8

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

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

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

Ответ 9

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

Ответ 10

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

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

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

Ответ 11

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

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

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

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

Ответ 12

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

Вот пример

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

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 нет, и вам нужно установить синхронизированное ключевое слово или вызвать метод синхронизации с коллекциями:

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

Ответ 13

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

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

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

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

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

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

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

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

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

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

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

Ответ 14

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

Ответ 15

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

Ответ 16

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

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

Ответ 17

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

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

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

Ответ 18

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

Пример:

    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
   } 

   }

Ответ 19

Я знаю, что это старый вопрос, но с моим быстрым просмотром ответов здесь я действительно не видел, чтобы кто-нибудь упоминал, что иногда метод synchronized может быть блокировкой неправильной.
Из Java Concurrency На практике (стр. 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;
}
}

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

Ответ 20

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

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

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

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

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

Ответ 21

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


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

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

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

Поэтому наилучшей практикой является использование модификатора 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
        }
    }
}

Ответ 22

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

Ну, это потокобезопасный синглтон:

// 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/

Ответ 23

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

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