ReentrantLock с CompletableFuture - PullRequest
       65

ReentrantLock с CompletableFuture

0 голосов
/ 14 января 2020

В мире CompletableFuture, если бы я хотел заблокировать переменную, я мог бы сделать это:

  private ReentrantLock myLock = new ReentrantLock();
  private List<UUID> myLockableList = new ArrayList<>();

  public void doStuff()
  {
    try
    {
      myLock.lock();
      myLockableList.clear();
    }
    finally
    {
      if (myLock.isLocked())
      {
        myLock.unlock();
      }
    }
  }

(Очевидно, это очень упрощенный пример, у меня есть другие методы, пытающиеся работать с myLockableList тоже).

Согласно ReentrantLock, может произойти сценарий 1 из 3 ios:

  • Получает блокировку, если она не удерживается другим потоком, и немедленно возвращается, устанавливая число удержаний блокировки до одного
  • Если текущий поток уже удерживает блокировку, то счетчик удержаний увеличивается на единицу, и метод возвращает немедленно
  • Если блокировка удерживается другим потоком, то текущий поток становится отключенным для целей планирования потоков и остается бездействующим до тех пор, пока блокировка не будет получена, и в это время счетчик удержания блокировки будет установлен в единицу. работая в том же потоке, я должен знать, заблокировал ли я ресурс, и если другой поток заблокировал ресурс, мне бы понравилось Чтобы дождаться, пока он станет доступным.

    Введите CompletableFutures ...

      private ReentrantLock myLock = new ReentrantLock();
      private List<UUID> myLockableList = new ArrayList<>();
    
      public CompletionStage<Void> doStuff()
      {
        return CompletableFuture.runAsync(() -> {
          try
          {
            myLock.lock();
            myLockableList.clear();
          }
          finally
          {
            if (myLock.isLocked())
            {
              myLock.unlock();
            }
          }
        });
      }
    

    Я надеюсь, что поведение для CompletableFuture будет таким же. Однако, CompletableFuture может выполнить вышеупомянутое с новым потоком, или (если мое понимание правильно), это могло бы использовать поток, который уже используется. Если произойдет второе (повторно используемый поток уже получил блокировку), тогда моя блокировка не будет приостанавливать поток и ждать блокировки, вместо этого он может фактически вернуться немедленно (похоже, что он получил блокировку)!

    Не могу найти окончательного ответа в документации. Правильно ли я понял ситуацию? Если да, то как мы должны блокировать ресурсы при использовании CompletableFuture?

    Большое спасибо за любые советы!

1 Ответ

1 голос
/ 14 января 2020

В дополнение к учебнику по параллелизму , рекомендованному в комментариях, Javado c для ReentrantLock содержит отличную информацию, которую стоит прочитать.

Это, например, :

ReentrantLock принадлежит потоку, который последний раз успешно блокировал, но еще не разблокировал его.

В вашем примере используется isLocked () , который Javado c говорит, что вы не должны делать:

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

Наконец, Javado c включает в себя отличный пример использования, показывающий lock() в блоке try, за которым следует unlock() в блоке finally.

Рекомендуется всегда следовать за вызовом. блокировать с помощью блока try, чаще всего в конструкции до / после, например:

 class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...

   public void m() {
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }
...