Mutex lock in java

Использование объекта мьютекса в Java

В этом уроке мы увидим различные способы реализации мьютекса в Java .

2. Мьютекс

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

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

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

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

3. Почему мьютекс?

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

public class SequenceGenerator < private int currentValue = 0; public int getNextSequence() < currentValue = currentValue + 1; return currentValue; >>

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

@Test public void givenUnsafeSequenceGenerator_whenRaceCondition_thenUnexpectedBehavior() throws Exception < int count = 1000; SetuniqueSequences = getUniqueSequences(new SequenceGenerator(), count); Assert.assertEquals(count, uniqueSequences.size()); > private Set getUniqueSequences(SequenceGenerator generator, int count) throws Exception < ExecutorService executor = Executors.newFixedThreadPool(3); SetuniqueSequences = new LinkedHashSet<>(); List> futures = new ArrayList<>(); for (int i = 0; i < count; i++) < futures.add(executor.submit(generator::getNextSequence)); >for (Future future : futures) < uniqueSequences.add(future.get()); >executor.awaitTermination(1, TimeUnit.SECONDS); executor.shutdown(); return uniqueSequences; > 

Как только мы выполним этот тестовый случай, мы увидим, что он в большинстве случаев терпит неудачу по причине, аналогичной:

java.lang.AssertionError: expected: but was: at org.junit.Assert.fail(Assert.java:88) at org.junit.Assert.failNotEquals(Assert.java:834) at org.junit.Assert.assertEquals(Assert.java:645)

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

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

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

4. Использование синхронизированного ключевого слова

Во-первых, мы обсудим ключевое слово synchronized , которое является самым простым способом реализации мьютекса в Java.

Каждый объект в Java имеет внутреннюю блокировку, связанную с ним. Метод /synchronized и | блок synchronized используют эту встроенную блокировку для ограничения доступа к критической секции только одним потоком за раз.

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

Давайте изменим getNextSequence на мьютекс, просто добавив ключевое слово synchronized :

public class SequenceGeneratorUsingSynchronizedMethod extends SequenceGenerator < @Override public synchronized int getNextSequence() < return super.getNextSequence(); >>

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

Итак, давайте теперь посмотрим, как мы можем использовать блок synchronized для синхронизации на пользовательском объекте мьютекса :

public class SequenceGeneratorUsingSynchronizedBlock extends SequenceGenerator < private Object mutex = new Object(); @Override public int getNextSequence() < synchronized (mutex) < return super.getNextSequence(); >> >

5. Использование ReentrantLock

Класс ReentrantLock был представлен в Java 1.5. Он обеспечивает большую гибкость и контроль, чем подход synchronized keyword.

Давайте посмотрим, как мы можем использовать ReentrantLock для достижения взаимного исключения:

public class SequenceGeneratorUsingReentrantLock extends SequenceGenerator < private ReentrantLock mutex = new ReentrantLock(); @Override public int getNextSequence() < try < mutex.lock(); return super.getNextSequence(); >finally < mutex.unlock(); >> >

6. Использование Семафора

Как и ReentrantLock , класс Semaphore также был представлен в Java 1.5.

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

Теперь давайте создадим еще одну потокобезопасную версию Генератора последовательностей с использованием Семафора :

public class SequenceGeneratorUsingSemaphore extends SequenceGenerator < private Semaphore mutex = new Semaphore(1); @Override public int getNextSequence() < try < mutex.acquire(); return super.getNextSequence(); >catch (InterruptedException e) < // exception handling code >finally < mutex.release(); >> >

7. Использование класса монитора Гуавы

До сих пор мы видели варианты реализации мьютекса с использованием функций, предоставляемых Java.

Однако класс Monitor библиотеки Guava от Google является лучшей альтернативой классу ReentrantLock . Согласно его документации , код, использующий Монитор , более удобочитаем и менее подвержен ошибкам, чем код, использующий ReentrantLock .

Во-первых, мы добавим зависимость Maven для Guava :

 com.google.guava guava 28.0-jre 

Теперь мы напишем еще один подкласс Генератора последовательностей , используя класс Monitor :

public class SequenceGeneratorUsingMonitor extends SequenceGenerator < private Monitor mutex = new Monitor(); @Override public int getNextSequence() < mutex.enter(); try < return super.getNextSequence(); >finally < mutex.leave(); >> >

8. Заключение

В этом уроке мы рассмотрели концепцию мьютекса. Кроме того, мы видели различные способы его реализации в Java.

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

Источник

Mutex lock in java

В Java ключевое слово synchronized используется для обеспечения взаимного исключения и гарантирует, что только один поток может получить доступ к критической секции кода в данный момент. Ключевое слово synchronized может использоваться с блоком кода или методом, и оно использует монитор объекта для синхронизации. В Java монитор — это синхронизационный конструкт, который ассоциирован с объектом. Каждый объект в Java имеет встроенный монитор, который можно использовать для синхронизации доступа к методам и полям этого объекта. Когда поток входит в синхронизированный блок кода или метод, он получает монитор, связанный с объектом, и удерживает его до тех пор, пока не выйдет из блока кода или метода. Мьютекс — это синхронизационный примитив, который используется для защиты общих ресурсов от одновременного доступа нескольких потоков. В Java ключевое слово synchronized может использоваться для обеспечения взаимного исключения, что аналогично функциональности мьютекса. Семафор — это синхронизационный примитив, который используется для управления доступом к общему ресурсу. В Java класс Semaphore может использоваться для реализации семафоров, которые можно использовать для контроля доступа к общим ресурсам несколькими потоками. Таким образом, ключевое слово synchronized использует монитор объекта, который аналогичен мьютексу. Оно не использует семафор напрямую, но семафор можно реализовать с помощью класса Semaphore в Java.

Странно, что в «Философия Java» Брюса Эккеля (4 издание) дают определения мьютекса и монитора наоборот: мьютекс — это механизм блокировки, а монитор — флаг, который используется для того, чтобы показать, что ресурс заблокирован: > Для решения проблемы соперничества потоков фактически все многопоточные схемы синхронизируют доступ к разделяемьм ресурсам. Это означает, что доступ к разделяе­мому ресурсу в один момент времени может получить только одна задача. Чаще всего это выполняется помещением фрагмента кода в предложение блокировки так, что одновременно пройти по этому фрагменту кода может только одна задача. Поскольку такое предложение блокировки дает эффект взаимного исключения (mutual exclusion), этот механизм часто называют мьютексом (mutex). > Каждый объект содержит объект простой блокировки (также называемый монитором), который автоматически является частью этого объекта (вам не придется писать для нее специального кода). Когда вы вызываете любой синхронизированный (synchronized) метод, объект переходит в состояние блокировки, и пока этот метод не закончит свою работу и не снимет блокировку, другие синхронизированные методы для объекта не могут быть вызваны.

Написано хорошо, понятно, но мне кажется, в конце немного понятия перепутаны. В статье написано, что мьютекс — это одноместный семафор, но судя по всему предыдущему объяснению, одноместным семафором является не мьютекс, а монитор. А мьютекс — это понятие, сходное не с семафором, а с разрешениями, которые семафор выдает. То есть, у монитора — один мьютекс, Соответственно, работает с данным кодом в один момент времени не более 1 потока. У семафора N разрешений. Соответственно, работают с данным кодом в один момент времени не более N потоков.

Монитор — невидимый для программиста кусок кода исполняемый jvm. Ну как бы и в чём прикол? Ну типа есть много всякого скрытого кода связанного с ключевыми словами или с другими языками программирования, это очевидно вроде. Но почему именно этот код имеет отдельное название и целое объяснение-пояснение. Есть умные люди в чате?) Объясните please зачем это знать 🙏

Прочитал несколько источников и понял так: Synchronized — ключевое слово, указывающее что для потоков, работающих с этим объектом/блоком кода, будет применяться механизм управления потоками (монитор).

Объект, указанный в ( ) содержит в себе маркер и может иметь 2 состояния: закрыт/открыт. Когда поток доходит до синхронизированного объекта/кода, он обращается к маркеру и запрашивает состояние. Это состояние контролирует монитор. Т.е. маркеры переключает монитор, а запрос делает поток. > Монитор — механизм контроля потоков при работе с сихронизированным объектом. Есть 2 типа механизма (монитора): мьютекс и семафор. 1. Механизм мьютекса ограничивает доступ к объекту/коду до 1 потока, т.е. одновременно с объектом может работать 1 поток. 2. Механизм семафора ограничивает доступ до N-го количества потоков одновременно, т.е. можно задать количество потоков. Мьютекс используется по умолчанию, а для задействования семафора нужно инициализировать класс Semaphor. У семафора через объект Semaphor можно настраивать: — количество потоков, которые могут одновременно работать с объектом;

 Semaphore(int permits) // конструктор 

— очередность работы потоков с объектом: последовательно (первый пришел — первый приступил к работе) или управление полностью отдается планировщику.

 Semaphore(. boolean fair) // конструктор 

Источник

Читайте также:  CSS Grids Gallery
Оцените статью