Блокировка — это механизм синхронизации потоков, подобный синхронизированным блокам. Блокировки реализованы внутри с использованием синхронизированных блоков. Поэтому мы можем использовать блокировки вместо синхронизированных ключевых слов в Java. Блокировка более гибкая и более сложная, чем синхронизированный блок.
Начиная с версии Java 5, JDK предоставляет несколько реализаций блокировок, таких как ReentrantReadWriteLock, ReentrantLock, StampedLock и т. Д.
1. Различия между синхронизацией и блокировками
1) Мы можем установить тайм-аут для получения доступа к ресурсам, используя метод Lock.tryLock (long timeout, TimeUnit timeUnit), тогда как это невозможно при синхронизации.
2) Синхронизированный блок должен полностью содержаться в одном процессе. Блокировка может содержаться в двух отдельных процессах: lock () и unlock ().
3) Поток, находящийся в состоянии «ожидания» для получения доступа к синхронизированному блоку, не может быть прерван. API блокировки предоставляет метод lockInterruptible (), который можно использовать для прерывания потока, когда он ожидает блокировки.
2. Реализация простой блокировки:
Функциональность простого приращения реализована с использованием синхронизированного ключевого слова.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
public class Counter { private int number = 0; public void increment() { synchronized(number) { return ++number; } }} |
Давайте преобразуем вышеуказанные программы, используя интерфейс Lock.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
public class Counter { private int number = 0; private Lock lock = new Lock(); public void increment() { lock.lock(); int newNumber = ++number; lock.unlock(); return new number; }} |
Здесь мы используем интерфейс блокировки вместо ключевого слова synchronized.
Прежде чем увеличивать число, мы должны были бы заблокировать, чтобы никто другой не мог ничего ввести в этот блок, пока блокировка не будет увеличена и снята.
3. Пути улучшения кода
Предположим, некоторые потоки читают данные, а некоторые записывают даты в некоторые ресурсы. Для чтения потоков, если один поток читает из ресурсов, если другой также читает из ресурса, это не вызовет никаких проблем, но если один поток пишет в ресурс, другой поток также записывает данные в ресурс, это вызовет проблему.
- Для операций чтения можно разрешить нескольким потокам читать данные из ресурсов, но не записывать потоки.
- Если один поток запрашивает доступ для чтения, один поток запрашивает доступ для записи, какой приоритет будет иметь, какой поток получит доступ к ресурсам?
- Если один поток запрашивается для доступа к чтению, второй поток запрашивается для доступа к записи, и если большее количество потоков запрашивает доступ к чтению, если мы разрешаем только чтение запрошенных потоков, поток записи должен будет ждать неопределенное количество времени, что приводит к Голодание.
- Чтобы избежать подобных сценариев, Java устанавливает некоторые правила, когда мы будем иметь доступ для чтения и записи.
3.1 ReadWriteLock
ReadWriteLock — реализация, предоставленная в Java от версии Java 5; у него есть два метода, read Lock () и write-lock ().
Метод Read Lock используется для операции чтения, тогда как метод write-lock () используется для операции записи.
3.2 Блокировка входа
Синхронизированные блоки в Java являются реентерабельными. Если поток Java входит в синхронизированный блок кода, возьмите Блокировку на объекте монитора, где блок синхронизирован. Затем поток может вводить другие блоки кода Java, синхронизированные на том же объекте монитора.
4. Давайте рассмотрим следующий сценарий:
- Тема 1 получает доступ для чтения.
- Thread2 запрашивает доступ для записи; он будет заблокирован, так как есть один читатель.
- Поток 1 снова запрашивает доступ для чтения, он будет заблокирован, поскольку существует один запрос на запись.
Thread1 и thread2 оба будут заблокированы, что приведет к тупиковой ситуации.
Чтобы сделать блокировки Reentrance, Java предоставляет еще одну реализацию для блокировок.
Используя блокировку входа, мы должны понимать некоторые варианты использования, такие как,
Для одного и того же объекта, в некотором методе, поток может запрашивать доступ для чтения, для того же объекта, может быть, в другом методе, он может запрашивать доступ для записи и наоборот.
4.1 Прочтите повторный вход:
Поток будет предоставлен Reentrance, если поток может получить доступ на чтение (нет доступа на запись и нет запросов на запись), или он уже имеет доступ на чтение. (независимо от написания запросов).
4.2 Написать повторный вход:
Запись Reentrance предоставляется, только если они имеют право на запись.
4.3 Читать, чтобы написать вход:
Иногда потоку необходимо прочитать, а также получить доступ к записи. Чтобы это разрешить, поток должен быть единственным читателем.
4.4 Написать для чтения вход:
Иногда потоку, который должен иметь доступ для записи, также необходим доступ для чтения. Автор должен всегда иметь доступ для чтения, если требуется. Если у потока есть права на чтение, никакие другие потоки не могут иметь права на чтение и запись, так что это не опасно.
Основываясь на вышеупомянутой концепции, давайте реализуем синхронизированную версию ArrayList.
ThreadSaftyArrayList.java
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
public class ThreadSaftyArrayList { private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private final Lock writeLock = readWriteLock.writeLock(); private final Lock readLock = readWriteLock.readLock(); private List list = new ArrayList(); public E get(int index) { readLock.lock(); try { return list.get(index); } finally { readLock.unlock(); } } public void set(E e) { writeLock.lock(); try { list.add(e); } finally { writeLock.unlock(); } } public static void main(String[] args) { ThreadSaftyArrayList threadSafeArrayList = new ThreadSaftyArrayList(); threadSafeArrayList.set("1"); threadSafeArrayList.set("2"); threadSafeArrayList.set("3"); System.out.println("Printing the First Element : "+threadSafeArrayList.get(1)); }} |
Примечание: вызов unlocks () из предложения finally:
Когда защита критического раздела с помощью ReadWriteLock и критического раздела может вызывать исключения, важно вызывать методы readUnlock () и writeUnlock () из предложения finally. Это гарантирует, что ReadWriteLock разблокирован, чтобы другие потоки могли его заблокировать.
псевдокод:
|
01
02
03
04
05
06
07
08
09
10
11
|
lock.lockWrite();try{ //do critical section code, which may throw an exception} finally { lock.unlockWrite();} |
5. Заключение
В текущем блоге мы узнали о блокировках, о том, как реализовать блокировки в Java, заменяя синхронизированные блоки. Мы также узнали о различных типах блокировок, таких как блокировка чтения и блокировка записи. Мы узнали о голодании, когда оно произойдет, а также о блокировках Reentrance, его использовании и классах реализации, предоставляемых в Java. Наконец, мы написали пример класса ThreadSafty Arraylist с использованием класса ReentrantReadWriteLock. Я надеюсь, что вышеприведенное руководство полезно для разработчиков Java и сообщества.
6. Загрузите блокировки в Java
Это был пример блокировки в Java.