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

StackOverflow https://stackoverflow.com/questions/574240

Вопрос

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

Это было полезно?

Решение

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

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

Пожалуй, единственное (но я бы не назвал это преимуществом) — вам не нужно включать ссылку на объект. 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
}

Другие советы

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

synchronized void foo() {
  ...
}

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

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

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

Плюсы:

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

Минусы:

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

Синхронный блок

Плюсы:

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

Минусы:

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

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

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

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

Object writeLock = new Object();

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

synchronized(writeLock){
  // do something
}

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

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

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

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

Обратите внимание, что конструкторы не могут быть синхронизированы — использование ключевого слова 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();
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Более подробное объяснение разницы вы можете найти здесь: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 на практике (стр.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;
}
}

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

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

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

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

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

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

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

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

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

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

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

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

// 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. Производительность снижена из-за использования ключевого слова Synchronized.

Минусы:

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

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

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

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

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

Следующий код показывает эти точки.Он также содержит потокобезопасный вектор, который можно заменить на ArrayList, чтобы показать, что многие потоки, добавляющие к Vector, не теряют никакой информации, в то время как то же самое с ArrayList может потерять информацию.0) Текущий код показывает потерю информации из -за условий гонки а) Комментируйте текущую обозначенную строку, и расстроен линию 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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top