Статьи

Замки в Java

Блокировка — это механизм синхронизации потоков, подобный синхронизированным блокам. Блокировки реализованы внутри с использованием синхронизированных блоков. Поэтому мы можем использовать блокировки вместо синхронизированных ключевых слов в 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. Тема 1 получает доступ для чтения.
  2. Thread2 запрашивает доступ для записи; он будет заблокирован, так как есть один читатель.
  3. Поток 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.

Скачать
Вы можете скачать полный исходный код этого примера здесь: Locks In Java