Почему бы не использовать try with lock в java?


Я прочитал эту тему, и эту статью в блоге О попробуйте с ресурсами блокировки, как вопрос выскочил у меня в голове. Но на самом деле, я бы предпочел попробовать с lock, я имею в виду без создания экземпляра lock. Это освободило бы нас от многословия.

lock.lock();
try {
    //Do some synchronized actions throwing Exception 
} finally {
    //unlock even if Exception is thrown
    lock.unlock();
}

Скорее будет выглядеть так :

? implements Unlockable lock ;
...
try(lock) //implicitly calls lock.lock() 
{
    //Do some synchronized actions throwing Exception 
} //implicitly calls finally{lock.unlock();}

Так что это будет не TWR, а просто какая-то шаблонная чистка.

Есть ли у вас какие-либо технические причины, чтобы предложить описывая Почему это не было бы разумной идеей?

EDIT: чтобы прояснить разницу между тем, что я предлагаю, и простым Блоком synchronized(lock){}, проверьте этот фрагмент:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;


public class Test {

        public static void main(String[] args) {
                ReentrantLock locker =new ReentrantLock();
                Condition condition = locker.newCondition();
                Thread t1 = new Thread("Thread1") {
                        @Override
                        public void run(){
                                synchronized(locker){
                                        try {
                                                condition.await();
                                        } catch (InterruptedException e) {
                                                Thread.currentThread().interrupt();
                                        }
                                        System.out.println("Thread1 finished");
                                }
                        }
                } ;
                Thread t2 = new Thread("Thread2") {
                        @Override
                        public void run(){
                                synchronized(locker){
                                        Thread.yield();
                                        condition.signal();
                                        System.out.println("blabla2");
                                }
                        }
                } ;
                t1.start();
                t2.start();
        }

}

Выполнение приведет к IllegalMonitorStateException, поэтому методы lock() и unlock() не вызываются неявно в блоке synchronized.

2 5

2 ответа:

Если вам пришлось иметь дело с таким простым случаем, когда шаблон блокировки/разблокировки был ограничен узкой областью, вы, вероятно, не хотите использовать более сложный класс Lock и, вероятно, вместо этого должны использовать ключевое слово synchronized. Тем не менее, если по какой-то причине вам это нужно с более сложным объектом Lock, он должен быть относительно прямолинейным, чтобы создать оболочку вокруг Lock, которая реализует AutoCloseable интерфейс, чтобы быть способен сделать именно это. Пример:

class AutoUnlock implements AutoCloseable {
  private final Lock lock;

  public static AutoUnlock lock(Lock lock) {
    lock.lock();
    return new AutoUnlock(lock);
  }

  public static AutoUnlock tryLock(Lock lock) {
    if (!lock.tryLock()) {
       throw new LockNotAcquiredException();
    }
    return new AutoUnlock(lock);
  }

  @Override
  public void close() {
    lock.unlock();
  }

  private AutoUnlock(Lock lock) {
    this.lock = lock;
  }
}

С помощью обертки, подобной приведенной выше, вы можете сделать:

try (AutoUnlock autoUnlock = AutoUnlock.lock(lock)) {
  // ... do whatever that requires the lock ...
}

Тем не менее, класс Lock обычно используется для очень сложных сценариев блокировки, где это не было бы особенно полезно. Например, объекты блокировки могут быть заблокированы в одной функции класса и позже разблокированы в другой функции (например, блокировка строки в базе данных в ответ на входящий удаленный вызов процедуры, а затем разблокирование этой строки в ответ на более поздний RPC), и таким образом, наличие такой обертки или создание Автоклавируемого замка само по себе будет иметь ограниченное применение для того, как он фактически используется. Для более простых сценариев более распространено просто использовать существующую параллельную структуру данных или использовать синхронизацию.

Этот ответ служит для объяснения поведения вашего редактирования. Цель создания synchronized Это блокировка монитора данного объекта, когда поток входит в блок (ожидание, если он недоступен) и освобождение его, когда поток выходит из блока.

Lock это абстракция более высокого уровня.

Lock реализации обеспечивают более широкие операции блокировки, чем может быть получен с помощью синхронизированных методов и операторов.

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

В фрагменте кода Вы пытаетесь вызвать

condition.signal();

В потоке, который не владеет Lock, из которого condition был созданный. Состоянияjavadoc

Реализация может (и обычно делает) требовать, чтобы текущий поток удерживает блокировку, связанную с этим Condition, когда этот метод называется . Реализации должны документировать это предварительное условие и любые действия, предпринятые, если замок не удерживается. Как правило, исключение, такое как IllegalMonitorStateException будет брошен.

Вот что здесь произошло.

Выполнение

synchronized (lock) {}

Делает ток блокировка потока (а затем освобождение) монитора на объекте, на который ссылается lock. Выполнение

lock.lock();

Заставляет текущий поток установить некоторое состояние внутри объекта, на который ссылается lock, которое идентифицирует его как владельца.