【问题标题】:Why not using a try with lock in java?为什么不在 java 中使用 try with lock 呢?
【发布时间】:2014-12-13 15:56:07
【问题描述】:

我读过this topic 和这篇blog article 关于尝试使用资源 锁的内容,因为这个问题突然出现在我的脑海中。 但实际上,我更喜欢尝试使用锁,我的意思是没有锁实例化。它会让我们从冗长的

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,而只是一些样板清洗。

您是否有任何技术理由建议描述为什么这不是一个合理的想法?

编辑:为了澄清我的建议和简单的 synchronized(lock){} 块之间的区别,请检查这个 sn-p :

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,因此在synchronized 块内不会隐式调用lock() 和unlock() 方法。

【问题讨论】:

  • synchronized 不就是这样做的吗?
  • 注意lock.lock()synchronized (lock)是完全不同的。
  • 是的,我在编辑中描述的用户案例正在等待退出线程空闲的条件。但显然这不是一种普遍的做法。

标签: java locking deadlock try-with-resources


【解决方案1】:

如果您必须处理这样一个简单的情况,其中锁定/解锁模式被限制在这样一个狭窄的范围内,您可能不想使用更复杂的 Lock 类,可能应该只是改为使用 synchronized 关键字。话虽这么说,如果由于某种原因您需要使用更复杂的 Lock 对象,那么创建一个实现 AutoCloseable 接口的 Lock 包装器就可以做到这一点。示例:

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),因此,拥有这样一个包装器或制作一个 Lock AutoCloseable 本身,对于它的实际使用方式来说是有限的。对于更简单的场景,更常见的是只使用现有的并发数据结构或使用同步。

【讨论】:

  • 对。 Lock 的一个目的是您可以跨方法边界获取和释放它。由于 OP 在他们建议的构造中不需要这个,synchronized 就足够了(显然它不会像Lock 对象那样锁定(获取监视器)。
  • 我明白你的意思。主要区别在于您必须实例化一个新对象...所以在这种情况下确实是一个简单的synchronized will be enough。但是,如果您需要 await() 在您的区块内的某个条件下呢?好吧,我只是一个初学者。感谢您的明确答复!
  • 对于像调用 await() 这样的事情,我更有可能在实际代码中使用现有的并发队列数据结构。但是,如果这不是一个选项,那么是的,那么你确实需要使用锁。
  • 但是同步不能区分读写。 imo,这使得它大大低于可重入读写锁。
【解决方案2】:

此答案用于解释您的编辑行为。 synchronized 的目的是在线程进入块时锁定给定对象的监视器(如果它不可用则等待)并在线程退出块时释放它。

Lock 是更高级别的抽象。

Lock 实现提供了比 可以使用同步的方法和语句获得。

您可以使用它来锁定跨方法边界。 synchronized 无法做到这一点,因此 Lock 不能单独使用 synchronized 实现,而且我从未见过使用它的实现。相反,他们使用其他模式,例如compare and swap。他们使用它在Lock 对象中以原子方式设置状态,该对象将某个线程标记为锁的所有者。

在您的代码 sn-p 中,您尝试调用

condition.signal();

在不拥有 Lock 的线程中创建了 conditionjavadoc 状态

实现可能(并且通常会)要求当前的 当此方法时,线程持有与此Condition 关联的锁 被称为。实施必须记录此前提条件和任何 未持有锁时采取的措施。通常,一个例外,例如 IllegalMonitorStateException 将被抛出。

这就是这里发生的事情。

执行

synchronized (lock) {}

使当前线程锁定(然后释放)lock 引用的对象上的监视器。执行中

lock.lock();

使当前线程在lock 引用的对象中设置一些状态,将其标识为所有者。

【讨论】:

  • 感谢 Sotirios 的评论。正是我想表明 synchronized 块与 lock.lock() 不同!在这点上我们完全同意。我只是认为您需要在对象 A 的方法中锁定/解锁,但您还需要在对象 A 的不同方法中使用单独的 lock() 和 unlock() 的用户案例更为常见。
猜你喜欢
  • 2015-11-26
  • 2017-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-23
  • 2011-12-22
  • 2017-02-13
相关资源
最近更新 更多