Java: когда синхронизировать методы и блоки - PullRequest
3 голосов
/ 14 октября 2011

Я понимаю, что ключевое слово synchronized используется для предотвращения одновременного доступа нескольких потоков к одному и тому же куску кода (либо к целому методу, либо к блоку синхронизированного кода в методе), и это помогает предотвратить неприятные ситуации.происходящие вещи, такие как тупики.

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

Я никогда не писалСинхронизированный метод или блок, и поэтому «видеть лес сквозь деревья» мне как-то сложно.Любые контекстные примеры приветствуются!Заранее спасибо.

Ответы [ 6 ]

3 голосов
/ 14 октября 2011

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

Вот пример из учебника по Java:

Весь раздел весьма поучителен:

2 голосов
/ 14 октября 2011

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

2 голосов
/ 14 октября 2011

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

вы хотите использоватьсинхронизированное ключевое слово, когда

a) если вы хотите, чтобы данные обрабатывались только одним потоком за раз (мьютекс)

b) если вы хотите, чтобы все изменения, сделанные предыдущим потоком, быливидимый другому потоку, когда он входит.

c) когда вы хотите, чтобы вся операция, выполняемая потоком, была атомарной (то есть полностью завершенной, а не оставленной в середине).

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

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

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

1 голос
/ 19 ноября 2011

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

public synchronized method( Param param ) {
   // do some work in here
}

public method( Param param ) {
    synchronized( this ) {
        // do some work here
    }
}

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

public synchronized method1( Param param ) {
    // do something unrelated to method 2
}

public synchronized method2( Param param ) {
   // do something unrelated to method 1
}

В этом случае method1 AND method2 будет разрешать поток только в одном из них одновременно, потому что синхронизированные методы блокируют это. Конечно, если бы мы синхронизировали это в блоке, это было бы то же самое. Тем не менее, мы не должны синхронизироваться на этом всегда. Вот способ сделать независимой блокировку method1 и method2.

Object method1Lock = new Object();
Object method2Lock = new Object();

public method1( Param param ) {
   synchronized( method1Lock ) {
      // do stuff
   }
}

public method2( Param param ) {
   synchronized( method2Lock ) {
      // do stuff
   }
}

Это важное различие, потому что теперь method1 и method2 могут выполняться одновременно, когда использование синхронизированных методов не позволит этого. Вы должны помнить, что method1 и method2 ДОЛЖНЫ быть независимыми в своем коде. Если method1 изменяет переменную экземпляра, а method2 считывает эту переменную, они не являются независимыми, и вы должны использовать синхронизацию метода. Однако, если method1 обращается к отдельным переменным экземпляра, чем method2, тогда этот метод может быть использован. Что это все значит? Ни одна техника не превосходит другую. Синхронизация методов проста и понятна, но может иметь более низкую производительность, потому что она слишком сильно блокируется. Блочная синхронизация решает эту проблему, но она более сложная.

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

1 голос
/ 14 октября 2011

Производитель-потребитель - это классический контекст, где синхронизация крайне необходима, если требуются правильные результаты.Производитель - это компонент кода (класс в Java), который производит некоторые модули, а Consumer - это другой компонент, который потребляет эти созданные модули.Производители и потребители должны работать независимо друг от друга, но при этом должны производить осмысленное поведение.

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

0 голосов
/ 21 декабря 2015

Используйте методы синхронизации, если:

(a) вы хотите добавить полный код метода как поточно-безопасный (проще говоря, этот код будет выполняться максимум одним потоком за раз)
(b) Вы не хотите использовать другую блокировку для другого метода.Добавление синхронизации к нестатическому методу требует блокировки объекта-экземпляра.При добавлении синхронизированных к нескольким методам все будут иметь одинаковую блокировку, что означает, что любой код всех синхронизированных методов будет выполняться максимум одним потоком за раз

Использовать блок синхронизации, если:

(a) вы хотите, чтобы только часть кода внутри метода была поточно-ориентированной.
(b) Вы хотите гибко выбирать собственную блокировку (каждый объект содержит блокировку монитора).

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

Прекрасный способ использования блока синхронизации показан в моем блоге здесь:

http://singletonjava.blogspot.com

...