是否有我可以使用的非重入读写锁?

2022-09-02 12:03:17

我需要一个不可重入的ReadWriteLock,因为该锁可能由与获取它的线程不同的线程释放。(当我开始间歇性地获得TravilMonitorStateException时,我意识到了这一点。

我不确定非重入是否是正确的术语。ReentrantLock 允许当前锁定的线程再次获取它。我不希望这种行为,因此我称之为“非重入”。

上下文是我有一个使用线程池的套接字服务器。每个连接没有线程。请求可能由不同的线程处理。客户端连接可能需要锁定一个请求并解锁另一个请求。由于请求可能由不同的线程处理,因此我需要能够在不同的线程中锁定和解锁。

为了解决这个问题,假设我需要保持这种配置,并且我确实需要锁定和解锁不同的请求,因此可能有不同的线程。

这是一个ReadWriteLock,因为我需要允许多个“读者”或一个排他性的“作家”。

看起来这可以使用 AbstractQueuedSynchronizer 编写,但我担心如果我自己编写,我会犯一些微妙的错误。我可以找到各种使用 AbstractQueuedSynchronizer 但不是 ReadWriteLock 的示例。

我可以采用OpenJDK ReentrantReadWriteLock源代码并尝试删除重入部分,但我担心我不会完全正确。

我查看了番石榴和Apache Commons,但没有找到任何合适的东西。Apache Commons有RWLockManager,它可能会做我需要的,但我不确定,它似乎比我需要的更复杂。


答案 1

信号量允许不同的线程执行许可证的获取和释放。独占写入等效于拥有所有许可证,因为线程会等到所有许可证都释放完毕,其他线程无法获取其他许可证。

final int PERMITS = Integer.MAX_VALUE;
Semaphore semaphore = new Semaphore(PERMITS);

// read
semaphore.acquire(1);
try { ... }
finally {
  semaphore.release(1);
}

// write
semaphore.acquire(PERMITS);
try { ... }
finally {
  semaphore.release(PERMITS);
}

答案 2

我知道你已经接受了另一个答案。但我仍然认为你会为自己创造一场噩梦。最终,客户将无法回来并释放这些许可证,您将开始怀疑为什么“作家”从不写信。

如果我这样做,我会这样做:

Client issues a request to start a transaction
The initial request creates a task (Runnable/Callable) and places it in an Executor for execution
The initial request also registers that task in a Map by transaction id

Client issues the second request to close the transaction
The close request finds the task by transaction id in a map
The close request calls a method on the task to indicate that it should close (probably a signal on a Condition or if data needs to be passed, placing an object in a BlockingQueue)

现在,事务任务将具有如下代码:

public void run() {
    readWriteLock.readLock().lock();
    try {
        //do stuff for initializing this transaction
        if (condition.await(someDurationAsLong, someTimeUnit)( {
            //do the rest of the transaction stuff
        } else {
            //do some other stuff to back out the transaction
        }
    } finally {
        readWriteLock.readLock.unlock();
    }
}

推荐