Java Lock API. Теория и пример использования

Java Lock API. Теория и пример использования

Обычно в многопоточной среде для достижения потокобезопасности, мы используем ключевое слово synchronized. Однако сегодня мы рассмотрим конкурента этому способу в виде Lock API.  

В большинстве случаев, ключевое слово synchronized является хорошим выбором, но все же имеет некоторые недостатки. Именно поэтому еще в Java 1.5 был введен Concurrency API и пакет  java.util.concurrent.locks c интерфейсом Lock и некоторыми дополнительными классами, которые усовершенствовали механизм блокировки.

Важные моменты в Concurrency Lock API

  1. Lock: Это базовый интерфейс в Lock API. Он обеспечивает все функции ключевого слова synchronized, добавляя новые методы для удобной работы. Например:
  • метод lock() используется для того, чтобы получить lock для работы;
  • метод unlock() — освободить lock;
  • метод tryLock() для ожидания лока на протяжении определенного времени;
  • метод newCondition() — создать Condition и т.п.
  1. Condition: Это похоже на wait-notify модель с рядом дополнительных функций. Объект Condition всегда создается с помощью объекта Lock.  Такой важный метод, как await() очень похож на wait(), а методы signal(), signalAll() похожи на notify() и notifyAll().
  2. ReadWriteLock содержит пару связанных локов: первый только для чтения, второй для записи. Лок для чтения может предоставлять доступ одновременно для нескольких потоков.
  3. Класс ReentrantLock — это наиболее используемая реализация интерфейса Lock. Эта реализация интерфейса Lock аналогична использованию ключевого слова synchronized. Кроме реализации интерфейса LockReentrantLock содержит ряд вспомогательных методов для работы с потоками.

Давайте рассмотрим использование Java Lock API на примере небольшой программы:

Допустим, у нас есть тестовый класс с синхронизированными методами обработки чего-либо.

Если поток входит в метод foo(), то происходит лок на объекте Test. Когда поток пытается выполнить метод bar(), то беспрепятственно его выполняет, потому что уже лочит объект Test. Это в точности похоже на использование метода synchronized(this).

А теперь давайте посмотрим простой пример, где можно и нужно заменить использование ключевого слова synchronized на Lock API.

И так, пусть у нас есть класс Resource с парочкой потокобезопасных методов и методов, где потокобезопасность не требуется.

А теперь берем класс, который реализует интерфейс Runnable и использует методы класса Resource.

Обратите внимание, что мы используем блок synchronized для доступа чтобы получить лок на объекте Resource.

А теперь давайте перепишем приведенную выше программу с использованием Lock API вместо ключевого слова synchronized.

Как видно из программы, мы используем метод tryLock(), чтобы убедиться в том, что поток ждет только определенное время. Если он не получает блокировку на объект, то просто логгирует и выходит.

Еще один важный момент. Мы используем блок try-finally, чтобы убедиться в том, что блокировка будет снята, даже если метод doSomething() бросит исключение.

Преимущества и недостатки каждого из способов или Lock vs synchronized

На основании вышеизложенной информации и простого примера использования Lock API и блока synchronized, мы можем сделать следующие выводы о преимуществах и недостатках каждого из способов или же просто указать на разницу между ними.

  1. Lock API обеспечивает больше возможностей для блокировки, в отличие от synchronized, где поток может бесконечно ожидать лок. В Lock API мы можем использовать метод tryLock(), чтобы ожидать лок только определенное время.
  2. Синхронизированный код намного чище и проще в поддержке. В случае использования Lock API мы вынуждены писать try-finally блок, чтобы убедиться в том, что блокировка будет снята, даже если между вызовами метода lock() и unlock().
  3. Блоки синхронизации могут покрывать только один метод, в то время как Lock API позволяет получить лок в одном методе, а снять его в другом.

Вот и все, что нужно знать о Lock API, его преимуществах и недостатках перед блоком synchronized, чтобы писать простые потокобезопасные программы на Java. Подробнее о многопоточности и параллелизму читайте в отдельном разделе сайта.

Комментарий “Java Lock API. Теория и пример использования

  1. Большое спасибо за внятное объяснение, для меня, как новичка, это очень важно. Возник следующий вопрос: если в классе SynchronizedLockExample объявит переменную Object lock = new Object() и переписать run(), как-то так
    public void run() {
    synchronized (lock) {
    resource.doSomething();
    }
    resource.doLogging();
    }
    То получится, ведь, то же, что и с использованием concurrent, или нет?

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *