【问题标题】:ReentrantReadWriteLock released by different thread不同线程释放的 ReentrantReadWriteLock
【发布时间】:2016-03-05 07:12:10
【问题描述】:

我正在单个服务器上实现乐观交易 (BOCC)。在提交时,根据当前数据库状态验证读取和写入集(如果自读取后状态已更改,则事务将中止)。如果验证成功,则将所有对象写入数据库。

不同对象集的验证(加上数据库更新)可以是并发的,但必须使用读写锁来保护重叠的对象集。

我使用ReentrantReadWriteLock 来保护验证,效果很好。现在我正在编写一个恢复机制,如果由于某些错误(在验证之后)不是所有对象都写入数据库,它会重复更新过程。

所以恢复会重复数据库写入,然后尝试释放锁(在恢复成功之后)。问题是我尝试从另一个线程释放锁(因为恢复是由另一个后台服务执行的),这会引发IllegalMonitorStateExceptionunlock 方法的注释验证了这种行为。

    /**
     * Attempts to release this lock.
     *
     * <p>If the current thread is the holder of this lock then
     * the hold count is decremented. If the hold count is now
     * zero then the lock is released.  If the current thread is
     * not the holder of this lock then {@link
     * IllegalMonitorStateException} is thrown.
     *
     * @throws IllegalMonitorStateException if the current thread does not
     * hold this lock
     */
    public void unlock() {

现在我的问题是:如果需要,我可以使用 Java 中的锁吗:

  • 独立于获取锁的线程释放锁。
  • 拥有读写锁(并可能从读写升级)
  • 不等待(阻塞)锁(我只使用tryLock)?

【问题讨论】:

  • 这些页面包含了很多关于java并发编程的基本信息。还包括关于 Reader/Writer Locks 的部分,但它太通用了,对我的用例没有帮助。
  • 非线程锁定锁和重入本身就是矛盾的(您需要一些其他标记来确定它是否仍然是同一个执行线程)。但是,如果您只需要一个不关心它创建的富线程的普通锁,那基本上就是一个信号量,如果您需要单独的读写锁,您将不得不自己实现它——这不是微不足道的,但有很多现有的实现看看。

标签: java multithreading concurrency transactions locking


【解决方案1】:

StampedLock 对我有用,可以从另一个线程解锁。我把它用作:

ReadWriteLock lock = new StampedLock().asReadWriteLock();

【讨论】:

    【解决方案2】:

    一般来说,将锁从不同的线程释放到请求锁的线程是一个坏主意。这样做会引发各种竞争条件和其他意外行为。例如,如果最初声明锁的线程开始对事物进行更改,因为它不知道其他线程已经处理了它并释放了锁,会发生什么?

    而是向原始线程发送一个信号,说明它已被处理并释放锁。

    tryLock 确实会让你尝试获得锁而无需等待。

    ReentrantReadWriteLockhttp://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html 似乎可以满足您的需要,只要您发出信号即可释放锁。

    您要么需要对原始线程进行重试,要么让原始线程等待(持有锁)直到重试完成,然后自行释放它们。只有声明锁的线程才能释放它,这几乎是线程的通用规则,因此不会有很多替代选项。

    当然,除非您想实现自己的锁(例如,最基本的 AtomicBoolean 可以被认为是写锁,以及 AtomicInteger 用于读锁)。这是你可能打开的一大罐蠕虫。正确获得线程和同步是困难

    【讨论】:

    • “一般来说,将锁从不同的线程释放到请求锁的线程是一个坏主意” - 这过于简单化了。异步编程是一种非常有用的范例,并且很容易导致锁从一个线程传递到另一个线程 - 与使用锁时一样多的风险。唯一需要特别小心的是,您使用的是不可重入锁。
    【解决方案3】:

    正如@TimB 指出的那样,一个线程释放(即破坏)另一个线程的锁是一件危险的事情一般来说

    但是,如果您想在允许的情况下实现自定义锁,您应该能够做到。如果您查看 java.util.concurrent.locks.ReentrantReadWriteLock 的实现,例如 (here),Sync.tryReleaseShared(int) 实现了释放锁的逻辑。如果你检查它,它实际上是在执行你所说的检查,并在代码中显式地抛出异常。虽然我不认为这会是一件简单的事情,但应该可以 将该行为更改为适合您的框架的行为。

    我知道任何现有的“易碎”锁实现吗?不。我在 GrepCode 中找不到。

    为什么?可能是因为其他人怀疑开锁的实用性(和安全性)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-19
      • 2016-02-28
      • 1970-01-01
      • 2015-06-16
      • 2014-06-14
      • 2021-03-27
      • 2014-12-01
      • 2011-09-13
      相关资源
      最近更新 更多