Со времен Java 5, пакет java.util.concurrent.locks содержит некоторые реализации блокировки, поэтому вам больше не нужно реализовать свои собственные блокировки. Но вам все еще нужно понять, как использовать эти замки.
Простой замок
Начнем с блока синхронизации в Java:
счетчик открытого класса {private int count = 0; public int inc () {synchronized (this) {return ++ count; }}}Вы можете видеть, что в методе INC () есть синхронизированный (этот) кодовый блок. Этот блок кода может гарантировать, что только один поток может выполнить return ++ считать одновременно. Хотя код в блоке синхронизированной синхронизации может быть более сложным, простой операции счета ++ достаточно, чтобы выразить значение синхронизации потоков.
Следующий класс счетчиков использует блокировку вместо синхронизации для достижения той же цели:
счетчик открытого класса {private Lock Lock = new Lock (); частный int count = 0; public int inc () {lock.lock (); int newCount = ++ count; lock.unlock (); вернуть NewCount; }}Метод lock () блокирует объект экземпляра блокировки, поэтому все потоки, которые вызывают метод lock () на объекте, будут заблокированы до тех пор, пока не будет вызван метод разблокировки () объекта блокировки.
Вот простая реализация класса блокировки:
Общественный счетчик класса {открытый класс Lock {private Boolean Islocked = false; public Synchronized void Lock () бросает прерывание {while (islocked) {wait (); } islocked = true; } public void unlock () {islocked = false; notify (); }}Обратите внимание на цикл Whance (Islocked) внутри, который также называется «Spin Lock». Когда Islocked True, резьба вызовов блокирует () блокирует и ждет при вызове wait (). Чтобы предотвратить возвращение потока от waist () без получения вызова () notify (), называемого ложным пробуждением), поток повторно проверит условие складывания, чтобы определить, можно ли безопасно продолжать выполнять или продолжать ждать снова, вместо того, чтобы думать, что поток можно безопасно продолжать выполнять после пробуждения. Если Islocked является false, текущий поток выходит из цикла (ISlocked) и устанавливается обратно на True, так что другие потоки, вызывающие метод Lock (), могут добавлять блокировки в экземпляр блокировки.
Когда поток завершает код в критическом разделе (расположенном между lock () и unlock ()), вызывается разблокировка (). Выполнение Unlock () будет переоценено в False и уведомляет (пробуждайте) один из потоков, которые назвали функцию wait () в методе Lock () и находятся в состоянии ожидания.
Воспроизведение замков
Синхронизированный блок синхронизации в Java повторно. Это означает, что если поток Java входит в блок синхронизированной синхронизации в коде и, таким образом, получает блокировку на трубе, соответствующей объекту синхронизации, используемому блоком синхронизации, то поток может ввести другой блок кода Java, синхронизированный тем же объектом трубопровода. Вот пример:
открытый класс reentrant {public synchronized over () {inner (); } public inner () {// что -то}}}}}}}}}}}Обратите внимание, что как Overt (), так и inner () объявляются синхронизированными, что эквивалентно синхронизированным (это) блокам в Java. Если поток вызывает Outter (), нет проблем вызывать inner () in outer (), потому что оба метода (кодовые блоки) синхронизируются одним и тем же объектом управления («This»). Если поток уже имеет блокировку на объекте трубы, он имеет доступ ко всем кодовым блокам, синхронизированным объектом трубы. Это повторно. Поток может ввести любой блок кода, синхронизированный с помощью блокировки, который он уже имеет.
Приведенная выше реализация блокировки не является повторной. Если мы переписываем класс Reentrant, как следующее, когда поток вызовет Outter (), он будет блокировать на lock.lock () метода inner ().
открытый класс reentrant2 {lock lock = new Lock (); public over () {lock.lock (); внутренний(); lock.unlock (); } public inner () {lock.lock (); // сделать что -нибудь lock.unlock (); }}Поток вызова OUTER () сначала заблокирует экземпляр блокировки, а затем продолжит вызовать inner (). В методе Inner () поток попытается снова заблокировать экземпляр блокировки, и действие будет выходить из строя (то есть поток будет заблокирован), потому что экземпляр блокировки уже заблокирован в методе Outter ().
Если разблокировать () не вызывается между lock () дважды, второй вызов для блокировки будет блокировать. После просмотра реализации Lock () вы обнаружите, что причина очевидна:
открытый класс Lock {boolean islocked = false; public Synchronized void Lock () бросает прерывание {while (islocked) {wait (); } islocked = true; } ...}Разрешено ли поток выйти, метод блокировки () определяется условиями в цикле while (спин блокировка). Состояние текущего суждения заключается в том, что операция блокировки разрешена только тогда, когда Islocked является ложным, не учитывая, какой резьбу блокирует ее.
Чтобы сделать этот класс блокировки, нам нужно немного изменить его:
открытый класс Lock {boolean islocked = false; Thread Bockedby = null; int lockedCount = 0; public Synchrinized void Lock () бросает прерывание {thread Callthread = Thread.currentThread (); while (islocked && lockedby! = Callthread) {wait (); } islocked = true; LockedCount ++; lockedby = callthread; } public synchronized void unlock () {if (thread.currentThread () == this.lockedby) {lockedCount--; if (lockedCount == 0) {islocked = false; notify (); }}} ...}Обратите внимание, что ток, в котором петля (Spin Lock) также учитывает потоку, которая заблокировала экземпляр блокировки. Если текущий объект блокировки не заблокирован (islocked = false), или текущий поток вызовов заблокировал экземпляр блокировки, то цикл WHIC не будет выполнен, а поток звонок Lock () может выйти из метода (примечание переводчика: «разрешено выйти из метода» в текущей семантике, что он не вызовет wait () и вызывает блокировку).
Кроме того, нам нужно записать количество раз, когда один и тот же поток многократно блокирует объект блокировки. В противном случае один вызов Unblock () будет разблокировать всю блокировку, даже если текущая блокировка была заблокирована много раз. Мы не хотим, чтобы блокировка была разблокирована до тех пор, пока вызов разблокировки () не достигнет количества раз при вызове соответствующего вызова lock ().
Теперь этот класс блокировки повторно.
Справедливость замка
Синхронизированные блоки Java не гарантируют порядок, в котором потоки пытаются их ввести. Следовательно, если несколько потоков продолжают конкурировать, чтобы получить доступ к одному и тому же блоку синхронизированной синхронизации, существует риск того, что один или несколько потоков никогда не получат доступ, то есть доступ всегда назначается другим потокам. Эта ситуация называется «Голод». Чтобы избежать этой проблемы, замки должны достичь справедливости. Замки, показанные в этой статье, внутренне реализованы с помощью синхронизированных блоков синхронизации, поэтому они не гарантированно будут справедливыми.
Call unlock () в конце
Если блокировка используется для защиты критической области, и критическая область может вызвать исключение, очень важно вызвать разблокировку () в конце. Это гарантирует, что объект блокировки может быть разблокирован, чтобы другие потоки могли продолжать его блокировать. Вот пример:
lock.lock (); try {// Do Critical Code, // который может добавить исключение}, наконец, {lock.unlock ();}Эта простая структура гарантирует, что объект блокировки может быть разблокирован, когда исключение брошено в критическую область. Если Unlock () не вызывается в операторе наконец, когда в критическом разделе добавлено исключение, объект блокировки останется в блокированном состоянии навсегда, что приведет к блокированию всех других потоков Lock () в объекте блокировки.
Выше приведено информация о многопоточной блокировке Java. Мы будем продолжать добавлять соответствующую информацию в будущем. Спасибо за поддержку этого сайта!