Какие замки на Java
Я не мог ответить на этот вопрос после прочтения <Java Condurrent Programming>, что показывает, что я недостаточно понимаю о концепции замков. Поэтому я снова просмотрел содержимое книги и внезапно почувствовал, что открыл свой лоб. Кажется, что лучший способ узнать - учиться с проблемами и решить их.
В Java существует два основных типа блокировки: внутренняя блокировка синхронизирована и отображает блокировку java.util.concurrent.locks.lock. Но если вы внимательно думаете об этом, кажется, что резюме не верно. Это должна быть серия замков, реализованных Java встроенными замками и одновременно.
Почему это говорит, потому что в Java все является объектом, и у Java есть блокировка, встроенная в каждый объект, который также можно назвать блокировкой объекта/внутренней блокировки. Соответствующая операция блокировки завершена посредством синхронизации.
Из -за недостатков в реализации синхронизированных и сложности параллельных сценариев кто -то разработал явную блокировку, и эти замки получены от java.util.concurrent.locks.locks. Конечно, он был встроен в JDK1.5 и более поздние версии.
синхронизированный
Во -первых, давайте посмотрим на синхронизированный, который используется чаще. Это также используется в моей ежедневной работе. Синхронизированный используется для обеспечения механизма блокировки для определенного кодового блока. Это неявно будет иметь блокировку в объектах Java. Этот замок называется внутренним или монитором. Поток автоматически приобретает эту блокировку перед входом блока, защищенного синхронизированным, до тех пор, пока блокировка не будет автоматически выпущена после завершения кода (или также может быть исключением). Встроенные замки являются взаимоисключающими. Замок может удерживать только одну резьбу одновременно, что также приведет к нескольким потокам, а потоки за замком будут заблокированы после удержания. Это позволяет безопасности потока кода обеспечить атомичность.
Повторный въезд
Поскольку встроенные блокировки Java взаимоисключают взаимоисключающие, а последующие потоки вызовут блокировку, что произойдет, если резьба, удерживающая блокировку, снова попадет при попытке получить блокировку? Например, одна из следующих ситуаций:
открытый класс baseclass {public synchronized void do () {System.out.println ("is base"); }} public class sonclass расширяет baseclass {public synchronized void do () {System.out.println ("is son"); super.do (); }} Sonclass son = new sonclass (); son.do ();В настоящее время метод DO полученного класса сначала удержит блокировку один раз, а затем снова войдет в блокировку и удерживает его при вызове Super.do (). Если замок является взаимоисключающим, в настоящее время он должен быть связан.
Но в результате не так, потому что внутренняя блокировка имеет характерную характеристику повторного входа, то есть блокировку реализует механизм повторного въезда, управление подсчетом ссылок. Когда резьба 1 содержит блокировку объекта A, ссылка на блокировку A будет рассчитана путем добавления 1. Затем, когда резьба 1 приобретает блокировку A снова, резьба 1 по -прежнему содержит блокировку A, то расчет добавит 1. Конечно, каждый раз, когда вы выходите из блока синхронизации, он будет уменьшен на 1, пока не будет 0.
Некоторые особенности синхронизированного
Как изменить код
Метод модификации
открытый класс baseclass {public synchronized void do () {System.out.println ("is base"); }}Это означает непосредственную блокировку метода, и вам необходимо получить блокировку при входе в этот блок метода.
Изменить кодовые блоки
открытый класс BaseClass {Private Static Object Lock = new Object (); public void do () {synchronized (lock) {System.out.println ("is base"); }}}Здесь диапазон блокировки уменьшается до некоторых кодовых блоков в методе, что улучшает гибкость блокировки. В конце концов, контроль гранулярности блокировки также является ключевой проблемой для блокировки.
Тип блокировки объекта
Я часто вижу, что некоторые коды используют синхронизированный в особых терминах, и посмотрите на следующий код:
открытый класс BaseClass {Private Static Object Lock = new Object (); public void do () {synchronized (lock) {}} public synchronized void dovoid () {} public synchronized static void dostaticvoid () {} public void Dostaticvoid () {synchronized (baseclass.class) {}}}Здесь есть четыре ситуации: изменение блока кода, изменение метода, изменение статического метода и изменение объекта класса BaseClass. Так каковы различия в этих ситуациях?
Изменить кодовые блоки
В этом случае мы создаем блокировку объекта, используя синхронизированный (блокировка) в коде, что означает использование встроенной блокировки объекта. В этом случае контроль блокировки передается объекту. Конечно, есть еще один способ сделать это:
public void do () {synchronized (this) {System.out.println ("is base"); }}Использование этого означает блокировку текущего объекта. Ключ к встроенной блокировке также упоминается здесь. Я предоставляю блокировку для защиты этого кода. Независимо от того, какая нить приходит, он столкнется с тем же замком.
Метод изменения объектов
Какова ситуация с этой прямой модификацией? Фактически, это похоже на изменение кодовых блоков, за исключением того, что это блокировка текущего объекта по умолчанию. Таким образом, это относительно просто и ясно писать код. Как упоминалось ранее, разница между изменчивыми блоками кода заключается в основном разницей между управлением гранулярностью.
Измените статические методы
Есть ли что -то другое в статических методах? Это действительно другое. Замок, приобретенная в это время, больше не является этим, и класс, на который указывает этот объект, является блокировкой класса. Поскольку информация класса в Java будет загружена в постоянную область метода, Global уникален. Это на самом деле обеспечивает глобальный замок.
Класс объект модифицированного класса
Эта ситуация на самом деле очень похожа при изменении статических методов, но это все же причина. Этот метод может обеспечить более гибкую гранулярность управления.
краткое содержание
Благодаря анализу и пониманию этих ситуаций, мы фактически видим, что основная концепция встроенной блокировки состоит в том, чтобы предоставить кусок кода блокировкой, который можно использовать для взаимоисключения, и играет функцию, похожую на переключатель.
Java также предоставляет некоторые реализации для встроенных замков. Основная особенность заключается в том, что Java - это все объекты, и у каждого объекта есть блокировка, поэтому вы можете выбрать, какую блокировку использовать в соответствии с ситуацией.
java.util.concurrent.locks.lock
Я смотрел на синхронизированный ранее. В большинстве случаев этого почти достаточно. Тем не менее, система становится все более и более сложной в одновременном программировании, поэтому всегда есть много сценариев, где синхронизированная обработка сложнее. Или, как указано в <Java Condurrent Programming>, блокировки в одновременном центре являются дополнением к внутренним замкам, обеспечивая более продвинутые функции.
Простой анализ java.util.concurrent.locks.lock
Этот интерфейс абстрагирует основную работу блокировки и, таким образом, позволяет блокировкам, полученным из блокировки, иметь эти основные характеристики: безусловные, велосипедные, временные, прерываемые. Более того, операции блокировки и разблокировки выполняются явно. Вот его код:
Общественный интерфейс Lock {void Lock (); void LockErrastible () бросает прерванное эктри; Boolean Trylock (); Boolean Trylock (долгое время, Unit Unit) бросает прерывания void unlock (); Условие newCondition ();} Reentrantlock
Reentrantlock - это блокировка повторного, даже имя настолько явное. Reentrantlock предоставляет аналогичную семантику для синхронизации, но Reentrantlock должен быть назван явно, например:
открытый класс BaseClass {Private Lock Lock = new Reentrantlock (); public void do () {lock.lock (); try {// ..} наконец {lock.unlock (); }}}Этот метод весьма ясен для чтения кода, но есть проблема, то есть, если вы забудете добавить попытку, наконец, или забыть написать Lock.unlock (), это приведет к тому, что замок не будет выпущен, что может привести к некоторым тупикам. Там нет риска синхронизации.
Трилок
Reentrantlock реализует интерфейс блокировки, поэтому он, естественно, имеет свои функции, включая Trylock. Trylock должен попытаться приобрести замок. Если замок был занят другими потоками, он немедленно вернет ложь. Если это не так, это должно быть занято и вернуть True, что означает, что блокировка была получена.
Другой метод Trylock содержит параметры. Функция этого метода состоит в том, чтобы указать время, что означает, что вы продолжаете пытаться получить блокировку в течение этого времени и сдаваться, если время не было получено.
Поскольку Trylock не всегда блокирует и ждет замков, он может избежать появления тупиков больше.
Замок
LockEntertible отвечает на прерывания, когда потоки приобретают блокировки. Если обнаружено прерывание, исключение прерывания отбрасывается кодом верхнего уровня. В этом случае механизм выхода предоставляется для блокировки круглой робин. Чтобы лучше понять прерывистую операцию блокировки, было написано демо, чтобы понять это.
пакет com.test; import java.util.date; import java.util.concurrent.locks.reentrantlock; открытый тестовый класс Testlock Interrustibuly {static reentrantlock lock = new Reentrantlock (); public static void main (string [] args) {Thread Thread1 = новый поток (new Runnable () {@Override public void run () {try {doprint («Поток 1 get Lock.»); do123 (); doprint («Поток 1 конец.»);} catch (прерывание Extreception e) {doprint («Поток 1 - recrupted.); Thread Think2 = новый поток (new Runnable () {@Override public void run () {try {doprint ("Thread 2 Get Lock."); Do123 (); doprint ("Thread 2 End.");} Catch (прерывание. Thread1.SetName ("Thread1"); Thread2.setName ("Thread2"); Thread1.start (); try {thread.sleep (100); // подождите некоторое время, чтобы заставить Thread1 выполнять перед Thread2} Catch (прерывание Exception e) {e.printStackTrace (); } thread2.start (); } private static void do123 () бросает прерывание {lock.lockEntertible (); doprint (thread.currentThread (). getName () + "заблокирован"); try {doprint (think.currentThread (). getName () + "dosoming1 ...."); Thread.sleep (5000); // Справка через несколько секунд, чтобы облегчить просмотр порядка потоков doprint (thread.currentThread (). GetName () + "dosoming2 ...."); doprint (thread.currentThread (). getName () + "завершен."); } наконец {lock.unlock (); }} private static void doprint (String Text) {System.out.println ((новый date ()). tolocalestring () + ":" + text); }}В приведенном выше коде есть два потока. Thread1 начинается раньше, чем Thread2. Чтобы увидеть процесс блокировки, заблокированный код спит в течение 5 секунд, чтобы вы могли почувствовать процесс первого и второго потока, вступающих в процесс сбора блокировки. Окончательный результат приведенного выше кода заключается в следующем:
2016-9-28 15:12:56: Поток 1 Получите блокировку.
2016-9-28 15:12:56: Thread1 заблокирован.
2016-9-28 15:12:56: Thread1 Dosoming1 ....
2016-9-28 15:12:56: Поток 2 Получите блокировку.
2016-9-28 15:13:01: Thread1 Dosoming2 ....
2016-9-28 15:13:01: Thread1 закончен.
2016-9-28 15:13:01: Thread1 выгружен.
2016-9-28 15:13:01: Thread2 заблокирован.
2016-9-28 15:13:01: Thread2 Dosoming1 ....
2016-9-28 15:13:01: нить 1 конец.
2016-9-28 15:13:06: Thread2 Dosoming2 ....
2016-9-28 15:13:06: Thread2 закончен.
2016-9-28 15:13:06: Thread2 выгружен.
2016-9-28 15:13:06: Тема 2 Конец.
Можно видеть, что Thread1 сначала получает блокировку, и Thread2 также получит блокировку позже, но Thread1 занял его в настоящее время, поэтому Thread2 не получает блокировку, пока Trade1 не выпустит блокировку.
** Этот код показывает, что поток, стоящий за блокировкой, чтобы получить блокировку, необходимо ждать, пока предыдущая блокировка будет выпущена перед получением блокировки. ** Но еще нет прерываемой функции, поэтому к этому добавляется какой -то код:
thread2.start (); try {thread.sleep (1000); } catch (прерывание Exception e) {e.printStackTrace ();} // прерывание потока2 в 1 секунду Thread2.interrupt ();После того, как Thread2 запускается, вызовите метод прерывания Thread2. Хорошо, сначала запустите код и посмотрите результаты:
2016-9-28 15:16:46: Поток 1 Получите блокировку.
2016-9-28 15:16:46: Thread1 заблокирован.
2016-9-28 15:16:46: Thread1 Dosoming1 ....
2016-9-28 15:16:46: Поток 2 Получите блокировку.
2016-9-28 15:16:47: нить 2 прерывается. <-Ответьте непосредственно на прерывание потока
2016-9-28 15:16:51: Thread1 Dosoming2 ....
2016-9-28 15:16:51: Thread1 закончен.
2016-9-28 15:16:51: Thread1 выгружен.
2016-9-28 15:16:51: нить 1 конец.
По сравнению с предыдущим кодом можно обнаружить, что Thread2 ждет, пока Thread1 выпустит блокировку, но сам Thread2 прерывается, и код, стоящий за Thread2, не будет продолжать выполнять.
ReadWritelock
Как следует из названия, это блокировка чтения. Таким образом, этот вид сценария приложений для блокировки чтения-записи можно понять. Например, волна данных в основном предусмотрена для чтения, и существует лишь относительно небольшое количество операций записи. Если используется блокировка Mutex, это приведет к конкуренции за блокировкой между потоками. Если каждый может прочитать его при чтении, заблокируйте ресурс, как только он будет записан. Такие изменения хорошо решают эту проблему, позволяя операции чтения улучшить производительность чтения, не влияя на операцию записи.
Один писатель может получить доступ к ресурсу с помощью нескольких читателей, или оба не могут быть выполнены одновременно.
Это абстрактный интерфейс для блокировки чтения и записи, определение блокировки чтения и блокировки записи.
Публичный интерфейс readwritelock { /*** Возвращает блокировку, используемое для чтения. * * @return Замок, используемый для чтения */ lock readlock (); /*** Возвращает блокировку, используемое для написания. * * @return Замок, используемый для написания */ lock writeLock ();}В JDK существует реализация ReenterTreadWriteLock, которая представляет собой блокировку Readrant Read-Write. Reentrantreadwritelock может быть построен в два типа: справедливо или несправедливо. Если не указано явно во время строительства, по умолчанию будет создан блокировка без FAIR. В режиме блокировки, не являющегося FIAR, порядок доступа к потоку неопределен, то есть его можно взломать; Его можно понизить с писателя до читателя, но читатель не может быть обновлен до писателя.
Если это справедливый режим блокировки, то опция передается потоку с самым длинным временем ожидания. Если поток считывания получает блокировку, и поток записи запрашивает блокировку записи, то приобретение блокировки чтения больше не будет получено до тех пор, пока операция записи не будет завершена.
Простой анализ кода на самом деле поддерживает блокировку синхронизации в повторном реэндтрителке, но он выглядит семантически как блокировка чтения и блокировку записи. Взгляните на его конструктор:
Public ReenterTreadWritelock (Boolean Fair) {Sync = Fair? new Fairsync (): new nonfairsync (); readerlock = new Readlock (это); writerlock = new WriteLock (this);} // Конструктор считываемого блокировки защищенного чтения (reentranTreadWritelock Lock) {sync = lock.sync;} // Конструктор записи блокировки защищенного writeLock (reenterTrareadWritelock) {sync = lock.sync;}Вы можете видеть, что объект Sync Lock of ReenterTreadWritelock фактически ссылается при построении. И этот класс синхронизации является внутренним классом ReenterTreadWritelock. Короче говоря, все блокировки чтения/записи выполняются с помощью синхронизации. Как это сотрудничает в отношениях между ними?
// Метод блокировки для чтения блокировки public void lock () {sync.acquireshared (1);} // Метод блокировки для написания блокировки public void lock () {sync.acquire (1);}Основное отличие состоит в том, что блокировка чтения получает общую блокировку, в то время как блокировка записи приобретает эксклюзивную блокировку. Здесь есть момент, который можно упомянуть, то есть для обеспечения того, чтобы Reentrantreadwritelock, как общие замки, так и эксклюзивные замки должны поддерживать подсчеты и повторные данные. Reentrantlock хранится с использованием состояния, а состояние может хранить только одну форму для формирования. Чтобы быть совместимым с проблемой двух замков, он делится на количество потоков, которые содержат общий блокировку или количество потоков, которые удерживают исключительное блокировку или подсчет возврата соответственно.
другой
Я написал большую статью, которая, как мне показалось, займет слишком много времени, чтобы написать, и есть еще несколько полезных замков:
Countdownlatch
Это установить счетчик, который проводится одновременно. Когда вызывающий звонок вызывает метод ожидания CountDownLatch, он будет блокировать, если текущий счетчик не будет 0. Вызов метода выпуска CountDownLatch может уменьшить количество подсчета, пока абонент, который вызовет, ожидает, не будет блокировать.
Семафон
Semaphore - это форма авторизации и лицензии, такая как настройка 100 лицензий, так что 100 потоков могут содержать замки одновременно, и если эта сумма превысит, она вернется к сбою.
Спасибо, что прочитали эту статью, я надеюсь, что это поможет вам. Спасибо за поддержку этого сайта!