I would like to migrate a Java function
protected static final Lock LOCK = new ReentrantLock();
public double calculate(...){
try {
LOCK.tryLock(20, TimeUnit.SECONDS);
...
}finally{
LOCK.unlock()
}
}
The same function in Scala:
protected final def LOCK = new ReentrantLock
def calculate(...): double = {
try{
LOCK.tryLock(20, TimeUnit.Seconds)
...
}finally{
LOCK.unlock()
}
}
The LOCK.unlock()
is always causing a IllegalMonitorStateException. I dont see any reason why this is happpening.
Can someone tell me where the problem is?
You should definitely make LOCK
a val
instead of a def
.
As it stands, you are recreating a new instance of ReetrantLock
every time you. Effectively what you are doing is this:
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()
}
It is obviously bound to fail.
You should do something like:
object MyClass {
private val LOCK = new ReentrantLock
}
class MyClass {
def calculate(...): double = {
try{
LOCK.tryLock(20, TimeUnit.Seconds)
...
}finally{
LOCK.unlock()
}
}
}
Which is the direct translation to scala of your original java code.
Finally, in his (now deleted) answer Jon Skeet rightly suggests:
You should only unlock the lock if you managed to acquire it - and the conventional pattern is to put the lock/tryLock call before the try. (It doesn't matter with tryLock(), but it does matter for lock(), so we might as well be consistent.)
Which gives:
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()
}
}
}
}