Reentrantlock отлично работает в Java, но вызывает исключение IllegalMonitorException в Scala.

Я хотел бы перенести функцию Java

protected static final Lock LOCK = new ReentrantLock();
public double calculate(...){
    try {
        LOCK.tryLock(20, TimeUnit.SECONDS);
        ...
    }finally{
        LOCK.unlock()
    }
}

Та же функция в Scala:

protected final def LOCK = new ReentrantLock
def calculate(...): double = {
    try{
        LOCK.tryLock(20, TimeUnit.Seconds)
        ...
    }finally{
        LOCK.unlock()
    }
}

LOCK.unlock() всегда вызывает исключение IllegalMonitorStateException. Я не вижу никакой причины, почему это происходит.

Может ли кто-нибудь сказать мне, в чем проблема?


person wurmi    schedule 08.04.2016    source источник
comment
Ваш метод calculate случайно не относится к классу/признаку? Поскольку в версии Java Lock является статическим, поэтому перевод этого в scala означал бы помещение его в объект, не в класс/признак, где определено calculate.   -  person Régis Jean-Gilles    schedule 08.04.2016
comment
Кроме того, и это, вероятно, причина ошибки, вы определяете LOCK как def, но на самом деле это должно быть val. В противном случае вы воссоздаете новую блокировку каждый раз, когда ссылаетесь на LOCK, эффективно делая блокировку бесполезной.   -  person Régis Jean-Gilles    schedule 08.04.2016
comment
Обычной практикой является получение блокировки перед попыткой, а не внутри.   -  person Alex Salauyou    schedule 08.04.2016
comment
метод calculate находится в абстрактном классе. Возможно, это связано с тем, что другой поток пытается разблокировать этот LOCK?   -  person wurmi    schedule 08.04.2016


Ответы (1)


Вы обязательно должны сделать LOCK val вместо def. В нынешнем виде вы воссоздаете новый экземпляр ReetrantLock каждый раз, когда вы. Фактически то, что вы делаете, это:

try {
    // Useless as we are creating a new lock
    (new ReentrantLock).tryLock(20, TimeUnit.SECONDS).tryLock(20, TimeUnit.SECONDS).tryLock(20, TimeUnit.SECONDS);
    ...
}finally{
    // Useless too, and will actually throw because we unlock a fresh (thus unlocked) lock
    (new ReentrantLock).unlock()
}

Очевидно, что это обречено на провал.

Вы должны сделать что-то вроде:

object MyClass {
  private val LOCK = new ReentrantLock
}
class MyClass {
  def calculate(...): double = {
      try{
          LOCK.tryLock(20, TimeUnit.Seconds)
          ...
      }finally{
          LOCK.unlock()
      }
  }
}

Это прямой перевод на scala вашего исходного кода Java.

Наконец, в своем (теперь удаленном) ответе Джон Скит справедливо предлагает:

Вы должны разблокировать блокировку только в том случае, если вам удалось ее получить, и обычный шаблон заключается в том, чтобы поместить вызов lock/tryLock перед попыткой. (Это не имеет значения для tryLock(), но имеет значение для lock(), так что мы можем быть последовательны.)

Который дает:

object MyClass {
  private val LOCK = new ReentrantLock
}
class MyClass {
  def calculate(...): double = {
      val gotLock = LOCK.tryLock(20, TimeUnit.Seconds)
      try {
          ...
      } finally {
        if (gotLock) {
          LOCK.unlock()
        }
      }
  }
}
person Régis Jean-Gilles    schedule 08.04.2016
comment
LOCK не может быть разрешен в «MyClass». Кажется, я что-то не упускаю - person wurmi; 08.04.2016