【问题标题】:unlocking a lock.tryLock with timeout用超时解锁 lock.tryLock
【发布时间】:2017-12-23 11:14:40
【问题描述】:

我有这个代码

Lock lock = new ReentrantLock();

void someMethod() {
    try {
        if (lock.tryLock(120, TimeUnit.SECONDS)) {
            // do stuff
        } else {
            // timed out 
            throw new RuntimeException("timeout");
        }
    } finally {
        lock.unlock();
    }
}

这工作正常,除非它超时。由于超时的线程不拥有锁,IllegalMonitorStateException 被抛出。 Lock接口中没有isHeldByCurrentThread。如果我不想投射到ReentrantLock,我必须使用一些丑陋的东西

...
} finally {
    if (lock.tryLock()) {
        lock.unlock();
        lock.unlock(); // twice because tryLock is called twice
    }
}

或

...
} finally {
    if ( !timeout ) {
        lock.unlock();
    }
}

还有更好的选择吗?谢谢

【问题讨论】:

    标签: java java.util.concurrent


    【解决方案1】:

    只有在你获得了锁后才能解锁:

    if (lock.tryLock(120, TimeUnit.SECONDS)) {
      try {
        // Do stuff.
      } finally {
        lock.unlock();
      }
    }
    

    注意:

    【讨论】:

      【解决方案2】:

      try-with-resources 是另一种选择(除了安迪的回答),但 Lock 不是 AutoCloseable 。

      因此您可以按照here in another SO question 和here 的说明编写一个包装器,然后您可以将try-with-resource 与此包装器一起使用。

      如果您不打算在应用程序的多个位置使用类似的构造,那么这可能不值得。

      编辑: 详细解答以解决 Sotirios Delimanolis 的担忧。

      在我看来,如果无法获取锁,OP 想要抛出 RuntimeException,并且对关闭 finally 块中的锁感到困惑,因为如果线程尚未持有锁,它可能会抛出 IllegalMonitorStateException。

      使用Lock 可以让您在程序员想要的任何地方灵活使用unlock(不一定是在try 块的末尾或方法的末尾),并且synchronized 关键字中缺少灵活性,但根据OP的代码sn-p,似乎他会在完成立即try块后解锁,所以AutoCloseable是有道理的。

      下面的代码解决了这两个问题,

      包装类

      import java.util.concurrent.TimeUnit;
      import java.util.concurrent.locks.ReentrantLock;
      
      public class CloseableReentrantLock extends ReentrantLock implements
              AutoCloseable {
      
          private static final long serialVersionUID = 1L;
      
      
          public CloseableReentrantLock timedLock() throws InterruptedException{
              if(this.tryLock(120, TimeUnit.SECONDS))
                  return this;
              else
                  throw new RuntimeException("timeout");
          }
      
          @Override
          public void close() throws Exception {
              this.unlock();
      
          }
      
      }
      

      客户

      try(CloseableReentrantLock lock = new CloseableReentrantLock().timedLock()) { //do stuff here
      }

      锁定获取场景:没有什么特别的事情发生,close 方法在 try-with-resource 块之后被调用并且锁定被解锁。这或多或少是synchronized 块的工作方式,但您无法使用synchronized 获得等待时间选项,但finally 代码混乱和程序员错过代码unlock 和finally 的机会不是syncronized 和 Closeable 包装器的情况也是如此。

      无法获取锁:在这种情况下,由于在 try-with-resource 中没有成功获取资源,并且 try-with-resource 本身会抛出异常, close 不会在这个资源上被调用,所以 IllegalMonitorStateException 不会在那里。 请参阅this question and accepted answer 以了解更多关于 try-with-resource 本身的异常。

      此示例代码进一步说明了这一点,

      public class TryResource implements AutoCloseable{
      
      
          public TryResource getResource(boolean isException) throws Exception{
              if ( isException) throw new Exception("Exception from closeable getResource method");
              else return this;
          }
      
          public void doSomething() throws Exception {
              System.out.println("In doSomething method");
          }
      
          @Override
          public void close() throws Exception {
              System.out.println("In close method");
              throw new Exception("Exception from closeable close method");
          }
      
      }
      

      和客户,

      public class TryResourceClient {
      
          public static void main(String[] args) throws Exception {
              try (TryResource resource = new TryResource().getResource(true)){
                  resource.doSomething();
              }
          }
      
      }
      

      【讨论】:

      • 如果不是AutoCloseable,那怎么选?即使使用包装器,它如何处理锁定失败?如果不值得努力,为什么要提出它?
      • 我已经更新了答案,以更多地说明我的意思以及我对 OP 要求的理解。在值得付出努力的部分,我不确定他是否会在他的应用程序中需要该语法一次或数千次,所以如果你需要它一千次,最好不要用 finally 块混淆你的代码并让解锁完成自动在一个地方。
      • 我只在一个地方有这个结构,所以会坚持使用嵌套的 try 块。但我同意这更清洁。感谢您花时间详细说明
      猜你喜欢
      • 2014-07-21
      • 2017-06-06
      • 1970-01-01
      • 1970-01-01
      • 2013-12-29
      • 2021-05-29
      • 2016-05-27
      • 2020-02-16
      • 1970-01-01
      相关资源
      最近更新 更多