Java5 уже содержит блокировки чтения и записи в пакете java.util.concurrent. Тем не менее, мы должны понимать принципы его реализации.
Реализация Java замок для чтения/записи
Давайте сначала дадим обзор условий для чтения и написания доступа к ресурсам:
Чтение поток не выполняет операцию записи, и ни один поток не требует операции записи.
Ни один поток не выполняет операции чтения и записи.
Если поток хочет прочитать ресурс, если ни один поток не записывается в ресурс и ни один поток запросов записывает в ресурс. Мы предполагаем, что запросы на операции на запись важнее, чем запросы на операции чтения, поэтому мы должны увеличить приоритет запросов на запись. Кроме того, если операции считывания часто происходят, и мы не увеличиваем приоритет операций на записи, то произойдет «голод». Поток, запрашивающий операцию записи, будет заблокирован до тех пор, пока все потоки чтения не будут разблокированы из readwritelock. Если разрешения на операцию чтения нового потока всегда гарантируются, поток, ожидающий операции записи, будет продолжать блокироваться, и результатом будет «голод». Следовательно, операция чтения может быть гарантированно продолжаться только тогда, когда ни один поток не блокирует readwriteLock для операций записи, и никакие потоки не запрашивают блокировку, чтобы быть готовым к операциям записи.
Когда другие потоки не читают и не записывают операции на общем ресурсе, поток может получить блокировку записи для общего ресурса, а затем записать операции на общем ресурсе. Неважно, сколько потоков запрашивают блокировки записи и в каком порядке, если вы не хотите обеспечить справедливость запроса на блокировку записи.
Согласно вышеуказанному описанию, блокировка чтения/записи просто реализована, а код следующим образом
открытый класс readwritelock {private int readers = 0; частные int writers = 0; private int writeRequests = 0; public Synchronized void lockread () бросает прерванную экспрессию {while (writers> 0 || writeRequests> 0) {wait (); } читатели ++; } public synchronized void unlockread () {readers--; notifyAll (); } public Synchronized void LockWrite () бросает прерывания {writeRequests ++; while (читатели> 0 || writers> 0) {wait (); } writeRequests--; писатели ++; } public synchronized void unlockwrite () бросает прерванное эктрипений {writers--; notifyAll (); }}В классе ReadWritelock читайте блокировку и запишите, каждый из них имеет метод для приобретения и отпускания блокировки.
Реализация блокировки чтения в блокировке (). Пока ни в одном потоке нет блокировки записи (авторы == 0), и ни один поток не требует блокировки записи (writeRequests == 0), все потоки, которые хотят получить блокировку чтения, могут быть успешно получены.
Реализация блокировки записи находится в LockWrite (). Когда поток хочет получить блокировку записи, он сначала добавит 1 к номеру запроса на блокировку записи (writeRequests ++), а затем определит, действительно ли он действительно получить блокировку записи. Когда нить поток не содержит блокировки чтения (читатели == 0), и ни один поток не содержит блокировки записи (авторы == 0), вы можете получить блокировку записи. Неважно, сколько потоков просят написать блокировки.
Следует отметить, что в обоих разблокировке, разблокировка, метод уведомления называется вместо уведомления. Чтобы объяснить эту причину, мы можем представить следующую ситуацию:
Если поток ждет, чтобы приобрести блокировку чтения, а поток ждет, чтобы приобрести блокировку записи. Если один из потоков, ожидающих блокировки чтения, пробуждается методом уведомления, но, поскольку все еще есть поток, запрашивающий блокировку записи (writeRequests> 0), пробужденный поток снова входит в состояние блокировки. Тем не менее, ни одна из поток, ожидающих замок записи, не разбудился, как будто ничего не произошло (примечание переводчика: потеря сигнала). Если вы используете метод notifyall, все потоки будут пробуждены, а затем определите, могут ли они получить замок, который они запросили.
Существует также одно преимущество в использовании notifyall. Если несколько потоков чтения ждут блокировки чтения, и ни один поток не ждет блокировки записи, после вызова UnlockWrite (), все потоки, ожидающие блокировки чтения, могут сразу же успешно приобрести блокировку чтения - вместо одного за раз.
Повторное въезд в чтение/запись блокировки
Заблокированная выше заблокированная блокировка/записи не является повторным и будет заблокирована, когда поток, который уже удерживает блокировку записи, снова запрашивает блокировку записи. Причина в том, что уже есть письменная тема - она сама. Также рассмотрим следующий пример:
Чтобы сделать ReadWriteLock повторным, необходимо сделать некоторые улучшения. Следующее будет обрабатывать возврат блокировки чтения и возврат блокировки записи соответственно.
Читать Lock Enterter
Чтобы сделать чтение блокировки readwritelock reentrant, мы должны сначала установить правила для Read Lock Reentrant:
Чтобы убедиться, что блокировка чтения в потоке будет повторно, либо соответствовать условиям для получения блокировки чтения (без запроса на записи или записи), либо уже удерживайте блокировку чтения (независимо от того, есть ли запрос на запись или нет). Чтобы определить, оставил ли поток блокировку считывания, можно использовать карту для хранения потока, который уже удерживал блокировку чтения, и количество раз, когда соответствующий поток приобретает блокировку чтения. Когда необходимо определить, может ли поток получить блокировку чтения, данные, хранящиеся в карте, используются для вынесения суждения. Ниже приведен модифицированный код методов блокировки и разблокировки:
открытый класс readWritelock {private Map <Thread, Integer> ReadingThreads = new Hashmap <Thread, Integer> (); частные int writers = 0; private int writeRequests = 0; public Synchronized void LockRead () бросает прерывание {Thread CallingThread = Thread.CurrentThread (); while (! cangrantreadaccess (callthread)) {wait (); } readingThreads.put (CallingThread, (getAccessCount (CallingThread) + 1)); } public synchronized void unlockread () {thread callthread = thread.currentThread (); int accessCount = getAccessCount (CallingThread); if (accessCount == 1) {readingThreads.remove (callthread); } else {readingThreads.put (CallingThread, (AccessCount -1)); } notifyall (); } private boolean cangrantreadaccess (поток Callthread) {if (writers> 0) вернуть false; if (isreader (callthread) return true; if (writeRequests> 0) return false; return true;} private int getReadAccessCount (Thread CallingThread) {integer accessCount = ReadingThreads.get (Callthread); if (accessCount == null) return 0; return account.intvalue ();} private boolean isReader (TrateThrath -ntoclive) {returnThardThore). ReadingThreads.get (CallingThread)! = NULL;В коде мы видим, что повторное введение блокировки чтения разрешено только в том случае, если ни в одном потоке нет блокировки записи. Кроме того, блокировки чтения Reentrant имеют более высокий приоритет, чем блокировки записи.
Напишите Lock Enterter
Повторное представление о блокировке записи разрешено только в том случае, если поток уже содержит блокировку записи (восстановите блокировку записи). Ниже приведен модифицированный код методов блокировки и разблокировки.
открытый класс readWritelock {private Map <Thread, Integer> ReadingThreads = new Hashmap <Thread, Integer> (); private int writeAccesses = 0; private int writeRequests = 0; Частная ветка witchThread = null; Public Synchrinized void LockWrite () бросает прерывание {writeRequests ++; Thread Callthread = Thread.currentThread (); while (! CangrantWriteAccess (Callthread)) {wait (); } writeRequests--; WriteAccesses ++; witchThread = CallingThread; } public synchronized void unlockwrite () бросает прерванную эксплуацию {writeAccesses--; if (writeAccesses == 0) {witchThread = null; } notifyall (); } private Boolean CangrantWriteAccess (Thread Callthread) {if (hasreaders ()) вернуть false; if (witchThread == null) вернуть true; if (! Iswriter (Callthread)) вернуть false; вернуть истину; } private boolean hasreaders () {return ReadingThreads.size ()> 0; } private Boolean Iswriter (Thread Callthread) {return witchThread == CallingThread; }}Обратите внимание на то, как справиться с этим при определении того, может ли текущий поток приобрести блокировку записи.
Читать обновление блокировки, чтобы записать блокировку
Иногда мы хотим, чтобы поток, который имеет блокировку чтения, чтобы получить блокировку записи. Чтобы разрешить такие операции, этот поток должен быть единственным потоком с блокировкой чтения. WriteLock () должен внести некоторые изменения для достижения этой цели:
открытый класс readWritelock {private Map <Thread, Integer> ReadingThreads = new Hashmap <Thread, Integer> (); private int writeAccesses = 0; private int writeRequests = 0; Частная ветка witchThread = null; Public Synchrinized void LockWrite () бросает прерывание {writeRequests ++; Thread Callthread = Thread.currentThread (); while (! CangrantWriteAccess (Callthread)) {wait (); } writeRequests--; WriteAccesses ++; witchThread = CallingThread; } public synchronized void unlockwrite () бросает прерванную эксплуацию {writeAccesses--; if (writeAccesses == 0) {witchThread = null; } notifyall (); } private boolean cangrantwriteaccess (поток Callthread) {if (isonlyReader (callthread)) return true; if (hasreaders ()) вернуть false; if (witchThread == null) вернуть true; if (! Iswriter (Callthread)) вернуть false; вернуть истину; } private boolean hasreaders () {return ReadingThreads.size ()> 0; } private Boolean Iswriter (Thread Callthread) {return witchThread == CallingThread; } private boolean isonlyReader (потока потока) {return readers == 1 && readingThreads.get (callthread)! = null; }}Теперь класс ReadWritelock может быть обновлен с блокировки чтения до блокировки записи.
Запишите блокировку, чтобы прочитать блокировку
Иногда потоки, в которых есть блокировки записи, также хотят получить блокировки чтения. Если поток имеет блокировку записи, то, естественно, другие потоки не могут иметь блокировку чтения или блокировку записи. Следовательно, нет опасности для потока, которая имеет блокировку записи, а затем получает блокировку чтения. Нам нужно только внести простой модификацию приведенного выше метода Cangrantreadaccess:
public class readwritelock {private boolean cangrantreadaccess (thread callthread) {if (iswriter (callthread)) return true; if (witchThread! = null) вернуть false; if (isreader (callthread) return true; if (writeRequests> 0) вернуть false; return true;}}Полная реализация reentrant readwritelock
Ниже приведена полная реализация ReadWritelock. Чтобы облегчить чтение и понимание кода, вышеупомянутый код был просто рефактор. Рефакторированный код выглядит следующим образом.
открытый класс readWritelock {private Map <Thread, Integer> ReadingThreads = new Hashmap <Thread, Integer> (); private int writeAccesses = 0; private int writeRequests = 0; Частная ветка witchThread = null; public Synchronized void LockRead () бросает прерывание {Thread CallingThread = Thread.CurrentThread (); while (! cangrantreadaccess (callthread)) {wait (); } readingThreads.put (CallingThread, (getReadAccessCount (CallingThread) + 1)); } private boolean cangrantreadaccess (thread callthread) {if (iswriter (callthread)) return true; if (haswriter ()) вернуть false; if (isreader (callthread)) вернуть true; if (haswriteRequests ()) вернуть false; вернуть истину; } public synchronized void unlockread () {thread callthread = thread.currentThread (); if (! isreader (callthread)) {throw new allogalMonitorStateException («Вызовный поток не« + »удерживайте блокировку чтения на этом readWritelock»); } int accessCount = getReadAccessCount (CallingThread); if (accessCount == 1) {readingThreads.remove (callthread); } else {readingThreads.put (CallingThread, (AccessCount -1)); } notifyall (); } public Synchronized void LockWrite () бросает прерывания {writeRequests ++; Thread Callthread = Thread.currentThread (); while (! CangrantWriteAccess (Callthread)) {wait (); } writeRequests--; WriteAccesses ++; witchThread = CallingThread; } public synchronized void unlockwrite () throws urpruptedException {if (! Iswriter (thread.currentThread ()) {throw new allodalMonitorStateException («Поток вызовов не« + »Hold the Write Lock на этом readwritelock»);} writeAccesses-if (writeAccess == 0). Boolean CangrantWriteAccess (Thread CallThread) {if (isonlyReader (CallingThread)) вернуть True; if (AccessCount == null) return 0; ReadingThreads.get (CallingThread)! = NULL; } private boolean haswriter () {return witchThread! = null; } private Boolean Iswriter (Thread Callthread) {return witchThread == CallingThread; } private boolean haswriteRequests () {return this.writeRequests> 0; }}Вызовать unlock () наконец -то
При использовании readwritelock для защиты критических зон, если критическая зона может добавить исключение, важно вызвать readunlock () и writeunlock () в блоке, наконец,. Это сделано для того, чтобы ReadWritelock может быть успешно разблокирован, а другие потоки могут запросить блокировку. Вот пример:
lock.lockwrite (); try {// Do Critical Code, который может выбросить исключение}, наконец, {lock.unlockwrite ();}Приведенная выше структура кода может гарантировать, что ReadWritelock также будет выпущен, когда исключение будет добавлено в критическую область. Если метод разблокировки написана не вызван в блоке, наконец, когда исключение будет добавлено в критическом разделе, ReadWriteLock останется в состоянии записи записи, что приведет к блокированию все потоки lockread () или lockwrite (). Единственным фактором, который может повторно записать ReadWriteLock, может быть то, что ReadWritelock является повторным. Когда исключение брошено, поток может успешно получить блокировку, затем выполнить критический раздел и снова вызовать (), который снова выпустит ReadWritelock. Но что, если нить больше не приобретает замок? Следовательно, вызов разблокировки, наконец, очень важен для написания надежного кода.
Выше приведено сборник многопоточной информации Java. Мы будем продолжать добавлять соответствующую информацию в будущем. Спасибо за поддержку этого сайта!