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


java.util.concurrent API предоставляет класс с именем Lock, который в основном сериализует элемент управления для доступа к критическому ресурсу. Это дает метод, такой как park() и unpark().

мы можем делать подобные вещи, если мы можем использовать synchronized ключевое слово и использование wait() и notify() notifyAll() методы.

мне интересно, какая из них лучше на практике и почему?

10 151

10 ответов:

если вы просто блокируете объект, я бы предпочел использовать synchronized

пример:

Lock.acquire();
doSomethingNifty(); // Throws a NPE!
Lock.release(); // Oh noes, we never release the lock!

вы должны явно сделать try{} finally{} везде.

в то время как с синхронизированным, это супер ясно и невозможно ошибиться:

synchronized(myObject) {
    doSomethingNifty();
}

что сказал:Locks может быть более полезным для более сложных вещей, где вы не можете приобрести и освободить таким чистым способом. Я бы честно предпочел не использовать голый Lockв первом место, и просто пойти с более сложным управлением параллелизмом, такие как CyclicBarrier или LinkedBlockingQueue, если они отвечают вашим потребностям.

у меня никогда не было причины использовать wait() или notify() но могут быть и хорошие.

мне интересно, какая из них лучше на практике и почему?

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

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

===

Я думаю документация делает хорошую работу по описанию различия между Lock и synchronized (выделено мной):

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

...

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

в то время как механизм определения области для синхронизироваться методы и заявления делает его гораздо легче программировать с монитором замки, помогает избежать многих распространенных ошибок программирования, связанных с замками, бывают случаи, когда вам нужно работать с замками более гибко. Например, **некоторые алгоритмы* для обхода одновременно доступных структур данных требуют использования "ручной" или "цепной блокировки": ты приобретаешь блокировку узла, то узел B, затем отпустите и приобрести С, затем отпустите кнопку B и приобрести D и так далее. Реализации блокировки интерфейс разрешить использование таких методов, как разрешение блокировки, которая будет получена и выпущена в разных областях и позволяет получать и освобождать несколько замков в любом порядке.

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

...

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

Lock реализации обеспечивают дополнительные функции над использованием синхронизированных методов и операторов, предоставляя неблокирующая попытка приобрести замок (tryLock()), попытка получить блокировку, которая может быть прервана (lockInterruptibly(), и попытка получить блокировку, которая может тайм-аут (tryLock (long, TimeUnit)).

...

вы можете достичь всего утилиты в java.утиль.одновременно сделать с примитивами низкого уровня, как synchronized,volatile или ждать/уведомления

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

параллельный API обеспечивает подход более высокого уровня, который проще (и как таковой безопаснее) использовать. В двух словах, вы не нужно использовать synchronized, volatile, wait, notify напрямую больше.

The замок сам класс находится на стороне нижнего уровня этого набора инструментов, вам даже не нужно использовать его напрямую (вы можете использовать Queues и семафор и прочее, и т. д., большую часть времени).

есть 4 основных фактора, почему вы хотите использовать synchronized или java.util.concurrent.Lock.

Примечание: синхронизированная блокировка - это то, что я имею в виду, когда говорю встроенную блокировку.

  1. когда Java 5 вышел с ReentrantLocks, они доказали довольно заметная пропускная способность разница тогда внутренняя блокировка. Если вы ищете более быструю блокировку механизм и работает 1.5 считают Дж.у.С. С ReentrantLock. Ява 6 внутреннего замка сейчас сходный.

  2. J.u.C.замок имеет различные механизмы для блокировки. Замок прерываемый - попытка блокировки до блокировки поток прерван; синхронизированный замок - попытка блокировки на определенную сумму времени и сдаться, если вы этого не сделаете succeed; tryLock-попытка блокировки, если какой-то другой поток держит замок сдается. Это все включено кроме простого замка. Внутреннеприсущая блокировка только предлагает простой блокировка

  3. стиль. Если 1 и 2 не попадают в категории того, что вы беспокоит большинство людей, включая меня, нашел бы внутреннеприсущая запирая семенатикс более легкая читать и менее многословно тогда Дж.у.С. Замок Замок.
  4. Несколько Условий. Объект блокировка может быть только уведомлена и ждали одного случая. Замка метод newCondition позволяет один замок иметь различные причины ждать или сигналить. Я еще к на самом деле нужна эта функциональность практика, но это хорошая функция для те, кому это нужно.

Я хотел бы добавить еще кое-что сверху Берт Ф ответ.

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

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

преимущества блокировка синхронизации из документации страница

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

  2. Lock реализации обеспечьте дополнительную функциональность над использованием синхронизированных методов и операторов путем обеспечивать неблокирующую попытку приобрести lock (tryLock()) попытка получить блокировку, которая может быть прервана (lockInterruptibly(), и попытка получить замок, который может timeout (tryLock(long, TimeUnit)).

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

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

ReentrantLock основные функции в соответствии с этим статьи

  1. возможность блокировки interruptibly.
  2. возможность тайм-аута во время ожидания блокировки.
  3. сила создать справедливый замок.
  4. API, чтобы получить список ожидающих потока для блокировки.
  5. гибкость, чтобы попытаться заблокировать без блокировки.

можно использовать ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock для дальнейшего получения контроля над гранулярной блокировкой операций чтения и записи.

помимо этих трех ReentrantLocks, java 8 предоставляет еще один Замок

StampedLock:

Java 8 поставляется с новым типом блокировки под названием StampedLock, который также поддерживает блокировки чтения и записи, как в примере выше. В отличие от ReadWriteLock методы блокировки StampedLock возвращают штамп, представленный длинным значением.

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

взгляните на это статьи при использовании различных типов ReentrantLock и StampedLock замки.

главное отличие-это справедливость, другими словами, запросы обрабатываются FIFO или может быть баржа? Синхронизация уровня метода обеспечивает справедливое или FIFO распределение блокировки. Используя

synchronized(foo) {
}

или

lock.acquire(); .....lock.release();

не обеспечивает справедливости.

Если у вас есть много разногласий по поводу блокировки, вы можете легко столкнуться с баржами, где новые запросы получают блокировку, а старые запросы застревают. Я видел случаи, когда 200 потоков прибывают в короткий срок для a замок и 2-й, чтобы прибыть был обработан последним. Это хорошо для некоторых приложений, но для других это смертельно.

посмотреть Брайана Гетца "на Java параллелизм на практике" книга, 13.3 для всестороннего обсуждения данной темы.

книга Брайана Гетца "параллелизм Java на практике", раздел 13.3: "...Как и ReentrantLock по умолчанию, внутренняя блокировка не дает никаких детерминированных гарантий справедливости, но статистические гарантии справедливости большинства реализаций блокировки достаточно хороши почти для всех ситуаций..."

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

  1. блокировка в одном методе и отпустите блокировку в другом методе.
  2. У вас есть два потока, работающие на двух разных фрагментах кода, однако первый поток зависит от второго потока, чтобы завершить определенный фрагмент кода, прежде чем он продолжит работу (в то время как некоторые другие потоки также работают одновременно). Общая блокировка может решить эту проблему вполне легко.
  3. реализация мониторов. Например, простая очередь, в которой методы put и get выполняются из множества различных потоков. Однако вы хотите, чтобы на круге ни один из тех же методов не накладывался друг на друга, ни оба метода put и get не могут перекрываться. В таком случае частный замок делает жизнь легче.

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

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

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

void randomFunction(){
.
.
.
synchronize(this){
//do some functionality
}

.
.
.
synchronize(this)
{
// do some functionality
}


} // end of randomFunction

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