Что такое монитор java
Перейти к содержимому

Что такое монитор java

  • автор:

Что такое монитор, мьютекс и семафор? Это одно и тоже или разные вещи?

Просто везде написано по-разному. И никак не могу понять, что по сути обозначает каждое из этих понятий. Например, у того же Эккеля есть такие строчки про это:

Для решения проблемы соперничества потоков фактически все многопоточные схемы синхронизируют доступ к разделяемым ресурсам. Это означает, что доступ к разделяемому ресурсу в один момент времени может получить только один поток. Чаще всего это выполняется помещением фрагмента кода в секцию блокировки так, что одновременно пройти по этому фрагменту кода может только один поток. Поскольку такое предложение блокировки дает эффект взаимного исключения, этот механизм часто называют мьютексом (MUTual Exclusion). В Java есть встроенная поддержка для предотвращения конфликтов в виде ключевого слова synchronized. Когда поток желает выполнить фрагмент кода, охраняемый словом synchronized, он проверяет, доступен ли семафор, получает доступ к семафору, выполняет код и освобождает семафор.

Сам я читал в одной статье (и пока придерживаюсь этого понимания), что мьютекс — это некий объект, который связан с каждым объектом в Джава, и который может принимать два состояния: занят и свободен. А про монитор в той же статей было написано, что это некий специальный механизм (по сути — кусок кода), который, используя мьютекс, регулирует доступ нитей к некоторому блоку кода (то есть он отвечает за захват нити ресурса, и позволяет только одной данной нити (которая захватила мьютекс) идти по данному, охраняемому монитором, блоку кода; соответственно, другим нитям монитор не даёт занять этот ресурс и этот блок кода; а когда нить выходит из этого блока кода, то монитор освобождает мьютекс и позволяет другим нитям войти в этот блок кода). Это правильное понимание? А что конкретно обозначают эти понятия в Джаве?

vchernogorov / _readme.md

Данный гист содержит основную информацию, которую нужно знать о Java Standart Edition.

  • Понятия многопоточности
  • Java инструменты многопоточности
  • Thread объект
  1. https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#yield()
  2. https://ru.wikipedia.org/wiki/%D0%9C%D1%8C%D1%8E%D1%82%D0%B5%D0%BA%D1%81
  3. https://ru.wikipedia.org/wiki/%D0%9C%D0%BE%D0%BD%D0%B8%D1%82%D0%BE%D1%80_(%D1%81%D0%B8%D0%BD%D1%85%D1%80%D0%BE%D0%BD%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F)
  4. https://en.wikipedia.org/wiki/Happened-before
  5. https://docs.oracle.com/javase/tutorial/essential/concurrency/index.html
  6. https://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601/192-1511873-1398145?ie=UTF8&*Version*=1&*entries*=0
  7. https://habrahabr.ru/company/golovachcourses/blog/256883/
  8. http://ru.stackoverflow.com/questions/1271/%D0%9A%D0%BB%D1%8E%D1%87%D0%B5%D0%B2%D0%BE%D0%B5-%D1%81%D0%BB%D0%BE%D0%B2%D0%BE-volatile-%D0%B2-java
  9. https://ru.wikipedia.org/wiki/%D0%90%D1%82%D0%BE%D0%BC%D0%B0%D1%80%D0%BD%D0%B0%D1%8F_%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D1%8F
  1. Monitor — высокоуровневый механизм взаимодействия и синхронизации процессов, обеспечивающий доступ к неразделяемым ресурсам.
    • При многозадачности, основанной на мониторах, компилятор или интерпретатор прозрачно для программиста вставляет код блокировки-разблокировки в оформленные соответствующим образом процедуры, избавляя программиста от явного обращения к примитивам синхронизации.
  2. Semaphore — семафор, самый простой тип блокировки, ограничивает количество потоков, которые могут войти в заданный участок кода.
    • Семафоры используются для синхронизации и защиты передачи данных через разделяемую память, а также для синхронизации работы процессов и потоков.
  3. Mutex — двоичный простейший семафор, который может находиться в одном из двух состояний: отмеченном или неотмеченном. Он отличается от семафора тем, что только владеющий им поток может его освободить, т.е. перевести в отмеченное состояние.
    • Задача мьютекса — защита объекта от доступа к нему других потоков, отличных от того, который завладел мьютексом.
    • В каждый конкретный момент только один поток может владеть объектом, защищённым мьютексом.
    • Если другому потоку будет нужен доступ к переменной, защищённой мьютексом, то этот поток блокируется до тех пор, пока мьютекс не будет освобождён.
  4. Reentrant mutex — мьютекс, который может быть залочен несколько раз одиним и тем же процессом/потоком без создания взаимной блокировки.
  5. Lock — блокировка, механизм синхронизации, позволяющий обеспечить исключительный достп к разделяемому ресурсу между несколькими потоками.
    • Мягкая блокировка — каждый поток пытается получить блокировку перед доступом к соответствующему разделямому ресурсу.
    • Обязательная блокировка — попытка несанкционированного доступа к заблокированному ресурсу будет прервана, через создание исключения в потоке, который пытался получить доступ.
  6. Spinlock — тип блокировки, который заставляет поток в бесконечном цикле пытаться получить доступ к блокировке.
    • Так как поток остается активным но не выполянет полезной работы, то использование данной блокировки не эффективно.
  7. Deadlock — взаимное исключение, блокировка, при которой несколько процессов находятся в состоянии бесконечного ожидания ресурсов, занятых самими этими процессами.
  8. Livelock — взаимное исключение, блокировка, при которой несколько процессов находятся в состоянии бесконечного зацикливания и не производят полезной работы.
  9. Process — процесс обладает автономной средой выполнения.
    • Каждый процесс, в частности, имеет собственную область памяти.
    • Процесс обычно воспринимается, как синоним выполнению программы или приложения. Однако бывает, что одна приложения занимает несколько процессов.
    • Большинство реализаций JVM запускаются в едином процессе.
    • Java приложение может создать дополнительный процесс с помощью ProcessBuilder объекта.
  10. Thread — потоки иногда называют легковесными процессами (lightweight processes). Потоки существуют внутри процесса — каждый процесс обладает хотя бы одним потоком.
    • Потоки делят ресурсы процесса, включая память и открытые файлы.
    • Приложение может быть как однопоточным, так и многопоточным, но всегда существует единственный «главный» поток, с которого запускается приложение.
  11. Interrupt — прерывание является указанием потоку остановить выпонение.
  12. Join — позволяет одному потоку ждать окончание выполнения другого потока.
  13. Thread safe — участок кода, который работает корректно как в однопоточной, так и в многопоточной среде.
    • Not thread safe — участок кода, который работает корректо только в однопоточной среде.
  14. Thread affinity — при старте потока, можно указать в рантайм среде, чтобы поток был привязан к определенному ядру.
  15. EDT (Event Dispatching Thread) — специальный поток, используемый для обработки событий из очереди событий. Такой подход является концептом событийно-ориентированного программирования.
    • Такие GUI фреймворки, как, например, AWT или Swing, используют EDT.
  16. Race condition — ошибка проектирования многопоточной системы или приложения, при которой работа системы или приложения зависит от того, в каком порядке выполняются части кода.
  17. Context switches — когда планировщик временно приостанавливает работу потока, чтобы активировать другой поток.
    • Частое явление в приложениях с кучей потоков, что очень дорого им обходится из-за:
    • Сохранение и восстановление выполняемого контекста
    • Непостоянное местоположение в памяти
    • Затрачивание CPU на планирование потоков, которое нужно тратить на их использование.
  18. Atomic action (Атомарная операция) — операция, выполняющаяся как единое целое, либо не выполняющаяся вовсе.
    • Атомарная операция не может быть прервана: она либо выполняется полностьбю, либо не выполняется вовсе.
    • Атомарная операция не производит изменений внешней среды во время выполнения, только по окончании.
    • Атомарная операция открыта влиянию только одного потока.
  19. Happened-before relationship — отношение «выполняется прежде» двух событий, которое гарантирует, что память, записанная событием A будет видна для события B, т.е. событие А завершает свою запись перед тем, как B начнет чтение.
    • Данное отношение транзитивно, иррефлексивно, антисимметрично.
    • Транзитивно — для любых 3-х событий a, b, c, если событие a происходит перед b, и b происходит перед c, тогда a должно происходить перед c.
    • Иррефлексивно — ни одно событие не должно происходить перед собой.
    • Антисимметрично — если событие a должно произойти перед событием b, то обратное невозможно.
  20. Critical section — участок кода, в котором производится доступ к общему ресурсу, который не должен быть одновременно использован более чем одним потоком.
    • Мьютекс является процедурой входа/выхода из критической секции.
  1. sleep() — заставляет поток остановить свое выполнение на указанное время. При этом поток не теряет контроль над монитором.
  2. start() — запускает данный поток из текущего потока.
    • Этот метод вызывает run() метод этого же потока.
  3. run() — наследники Thread должны перегружать данный метод. Он вызывается при старте потока.
  4. interrupt() — прерывает выпонение потока.
    • InterruptedException — данный поток получит это исключение, если произошло успешное прерывание потока.
    • ClosedByInterruptException — если данный поток был заблокирован I/O операцией, то поток получит это исключение по окончанию прерывания.
  5. setPriority() — изменяет приоритет данного потока.
    • Минимальный приоритет — 1, максимальный — 10.
  6. join() — заставляет поток ждать не более чем указанное время, чтобы завершиться.
    • Реализация данного метода использует цикл с вызовом wait() , который вызывается пока isAlive . После завершения потока вызывается notifyAll() метод.
    • Он создан для того, чтобы приложения не использовали методы wait() , notify , notifyAll методы из Thread сущностей, так как это не рекомендованно.
    • Если не указывать время или указать 0, то поток будет ждать вечно чтобы умереть. пичаль.
  7. setDaemon() — отмечает данный поток как поток-демон или пользовательский поток.
    • JVM отрубается если все запущенный потоки являются демонами.
    • Этот метод должен выполняться перед стартом потока.
  8. yield() — указывает планировщику, что текущий поток закончил свое выполнение и готов перейти в пользование процессора. Планировщик однаком может игнорировать это указание.
    • Использование данного метода редко является уместным. Он может использоваться для дебага или тестирования.

Java инструменты многопоточности

  1. JMM (Java Memory Model) — описывает поведение потоков в среде исполнения Java.
    • Однопоточные программы выолняются псевдопоследовательно, то есть в реальности процессор может выполнять несколько операций за такт, заодно изменив их порядок, однако все зависимости по данным остаются, так что поведение не отличается от последовательного.
  2. java.lang.Thread — каждый поток ассоциируется с объектом этого класса.
    • Thread.sleep() отдает команду текущему потоку приостановить выполнение на указанное время.
    • Thread.interrupted() возвращает true , если выполнение потока было прервано.
    • thread.join() позволяет одному потоку ждать окончания выполнения другого (т.е. текущий ждет выполнения потока thread ).
  3. java.lang.Runnable — интерфейс определяет единственный метод run , который должен содержать код, который будет выполняться в потоке.
  4. volatile — данный модификатор указывает компилятору, что чтение перемнной будет производиться прямо из памяти, что позволяет нескольким потокам видеть последнее значение переменной.
  5. java.util.concurrent.locks.Lock — интерфейс, реализации которого предполагают более обширные операции блокировки, чем предоставляемые synchronized методами и блоками.

Что такое «монитор» в Java?

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

Удобно представлять монитор как id захватившего его объекта. Если этот id равен 0 – ресурс свободен. Если не 0 – ресурс занят. Можно встать в очередь и ждать его освобождения.

В Java у каждого экземпляра объекта есть монитор, который контролируется непосредственно виртуальной машиной. Используется он так: любой нестатический synchronized -метод при своем вызове прежде всего пытается захватить монитор того объекта, у которого он вызван (на который он может сослаться как на this ). Если это удалось – метод исполняется. Если нет – поток останавливается и ждет, пока монитор будет отпущен.

Мютекс и монитор

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

— Я готов слушать.

Мютекс – это специальный объект для синхронизации нитей/процессов. Он может принимать два состояния – занят и свободен. Если упростить, то мютекс – это boolean-переменная, которая принимает два значения: занят(true) и свободен(false).

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

— Т.е. мютекс – это как табличка на двери – занят/свободен?

— Да. И такой мютекс прикреплен к каждому объекту в Java. Прямой доступ к мютексу есть только у Java-машины. От программиста он скрыт.

— А как же тогда его использовать?

— А работать с мютексом в Java можно посредством монитора.

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

В Java монитор реализован с помощью ключевого слова synchronized.

Когда ты пишешь блок synchronized, то компилятор Java заменяет его тремя кусками кода:

1) В начале блока synchronized добавляется код, который отмечает мютекс как занятый.

2) В конце блока synchronized добавляется код, который отмечает мютекс как свободный.

3) Перед блоком synchronized добавляется код, который смотрит, если мютекс занят – то нить должна ждать его освобождения.

Вот как это примерно работает:

synchronized(object)  object.doJob(); >
while (object.mutex) Thread.sleep(1); object.mutex = true; object.doJob(); object.mutex = false;

На самом деле логика там другая и сложнее, но это уже детали.

— А можно подробности?

— Пока не научишься правильно этим пользоваться – в детали лезть нет смысла.

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

— Не очень. Просто все быстро забывается…

— Чем меньше практики, тем меньше пользы и от теории.

Вот уровню к 40 научишься всем этим пользоваться, и я тебе объясню, как все устроено на самом деле. А пока — учись просто правильно всем пользоваться. Ясно?

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *