我可以使用不可重入的ReadWriteLock吗?

时间:2012-05-22 23:19:40

标签: java concurrency locking

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

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

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

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

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

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

我可以使用OpenJDK ReentrantReadWriteLock源并尝试删除可重入部分,但我又担心我不会理解它。

我看过Guava和Apache Commons,但没找到合适的东西。 Apache Commons有RWLockManager可能会做我需要的但是我不确定它看起来比我需要的更复杂。

4 个答案:

答案 0 :(得分:27)

信号量允许不同的线程执行许可的获取和释放。独占写入相当于具有所有许可,因为线程等待直到所有许可都被释放并且其他线程不能获得额外的许可。

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);
}

答案 1 :(得分: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();
    }
}

答案 2 :(得分:1)

不完全确定你需要什么,尤其是为什么它应该是一个读写锁,但如果你有许多线程需要处理的任务,并且你不希望它同时被处理/访问,我实际上使用的是ConcurrentMap(等等)。

您可以从地图中删除任务或用特殊的“锁定对象”替换它以表明它已被锁定。您可以将具有更新状态的任务返回到地图以让另一个线程接管,或者您可以将任务直接传递给下一个线程并让它将任务返回到地图。

答案 3 :(得分:1)

他们似乎已弃用com.sun.corba.se.impl.orbutil.concurrent.Mutex;

我的意思是谁在正确的思想中认为我们不需要非重入锁定。在这里,我们浪费时间来争论可重入的定义(每个框架的含义可能会略有变化)。是的,我想在同一个线程上尝试锁定是不是这么糟糕?它不会陷入僵局,因为它不好了。锁定在同一线程中的非重入锁定非常有用,可以防止用户快速重复按下同一按钮的GUI应用程序出错。在那里,做到了,QT是对的......再次。