为什么ReentrantReadWriteLock非公平时写线程拿不到锁?
从这个问题如何理解“非公平”模式ReentrantReadWriteLock?,我认为所有线程都有相同的机会获得锁,无论哪个先到。
所以我写了这样的代码来测试一下:
public static void main(String[] args) {
ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
final ReadLock readLock = lock.readLock();
final WriteLock writeLock = lock.writeLock();
// hold the write lock 3s at first
new Thread() {
public void run() {
writeLock.lock();
System.out.println(Thread.currentThread().getName() + " got the write lock");
quietSleep(3);
writeLock.unlock();
System.out.println(Thread.currentThread().getName() + " released the write lock");
};
}.start();
// a thread want to get the read lock 1s later
new Thread() {
public void run() {
quietSleep(1);
readLock.lock();
System.out.println(Thread.currentThread().getName() + " got the read lock");
};
}.start();
// 1000 threads want to get the write lock 2s later
for (int i = 0; i < 1000; i++) {
new Thread() {
public void run() {
quietSleep(2);
writeLock.lock();
System.out.println(Thread.currentThread().getName() + " got the write lock");
};
}.start();
}
}
private static void quietSleep(int seconds) {
try {
Thread.sleep(seconds * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
首先,有一个线程获得了写锁,并保持了3s。这段时间有一个线程想要获取读锁,然后有1000个线程想要获取写锁。
由于ReentrantReadWriteLock默认使用非公平模式,所以我认为写线程有很大的机会获得写锁。但我运行了很多次,每次读取线程都赢了!
输出是:
Thread-0 got the write lock
Thread-0 released the write lock
Thread-1 got the read lock
我对“不公平”的理解错误吗?
更新 根据 paxdiablo 的回答,我将代码修改为:
new Thread() {
public void run() {
quietSleep(1);
writeLock.lock();
System.out.println(Thread.currentThread().getName() + " got the write lock");
};
}.start();
for (int i = 0; i < 1000; i++) {
new Thread() {
public void run() {
quietSleep(2);
readLock.lock();
System.out.println(Thread.currentThread().getName() + " got the read lock");
};
}.start();
}
现在有一个线程想要写锁,1000 个读线程想要读锁。但输出是:
Thread-0 got the write lock
Thread-0 released the write lock
Thread-1 got the write lock
似乎仍然是“先到先得”。
From this question How to understand the “non-fair” mode of ReentrantReadWriteLock?, I think all threads have the same opportunity to get the lock no matter which comes first.
So I write this code to test it:
public static void main(String[] args) {
ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
final ReadLock readLock = lock.readLock();
final WriteLock writeLock = lock.writeLock();
// hold the write lock 3s at first
new Thread() {
public void run() {
writeLock.lock();
System.out.println(Thread.currentThread().getName() + " got the write lock");
quietSleep(3);
writeLock.unlock();
System.out.println(Thread.currentThread().getName() + " released the write lock");
};
}.start();
// a thread want to get the read lock 1s later
new Thread() {
public void run() {
quietSleep(1);
readLock.lock();
System.out.println(Thread.currentThread().getName() + " got the read lock");
};
}.start();
// 1000 threads want to get the write lock 2s later
for (int i = 0; i < 1000; i++) {
new Thread() {
public void run() {
quietSleep(2);
writeLock.lock();
System.out.println(Thread.currentThread().getName() + " got the write lock");
};
}.start();
}
}
private static void quietSleep(int seconds) {
try {
Thread.sleep(seconds * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
At first, there is a thread got the write lock, and held it for 3s. During this time, a thread want to get the read lock, and then 1000 threads want to get the write lock.
Since ReentrantReadWriteLock uses non-fair mode by default, I think the write threads have a great opportunity to get the write lock. But I run it many times, and each time read thread won!
The output is:
Thread-0 got the write lock
Thread-0 released the write lock
Thread-1 got the read lock
Do I understanding "non-fair" wrong?
UPDATE
Per paxdiablo's answer, I modified my code to:
new Thread() {
public void run() {
quietSleep(1);
writeLock.lock();
System.out.println(Thread.currentThread().getName() + " got the write lock");
};
}.start();
for (int i = 0; i < 1000; i++) {
new Thread() {
public void run() {
quietSleep(2);
readLock.lock();
System.out.println(Thread.currentThread().getName() + " got the read lock");
};
}.start();
}
Now there is a thread want the write lock and 1000 read threads want the read lock. But the output is:
Thread-0 got the write lock
Thread-0 released the write lock
Thread-1 got the write lock
Seem it's still "first-come-first-get".
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
非公平只是意味着它不必以排队的方式分发锁(先到先得)。对于如何分发,它不做任何其他保证。事实上,如果需要的话,它可能仍然以排队的方式分发它们。
它可能更喜欢将锁分发给读者,因为多个读者可以同时拥有锁,但如果一个写入者获得了锁,则所有读者和写入者都会被阻止。
很久以前,我曾经不得不实现一个读取器/写入器互斥锁,它有多种模式,具体取决于您需要实现的目标:
听起来第一个是您的系统可能正在做的事情(我说“可能”是因为您的代码可能会确定性地运行,尽管具有线程性质,即每次都相同)。
如果您想了解公平和非公平之间的区别,请尝试以下操作。
在公平模式下,它应该是
RWRRRRRRRRRR
。这是因为除了第一个读锁之外的所有锁都将等待,直到获得并释放写锁(写首先进行,因为这是公平的)。在非公平模式下,您可能会看到
RRRRRRRRRRRW
,读锁可能都被允许跳转到写锁前面,因为它们不会干扰第一个读锁,公平性见鬼了:- )当然,公平的概念可能因作者而异。允许读取偷偷地在写入之前进行但有限制的算法可能符合规则。例如,一旦请求写锁,则在排队在写锁后面之前,只允许另外 5 个读锁潜入。
我并不是说有人实施过这种做法,但这肯定是一个可行的选择,可以平衡效率与公平。
Being non-fair just means it doesn't have to hand out the lock in a queued manner (first come, first served). It makes no other guarantees about how it will be handed out. In fact, it may still hand them out in a queued manner if it wants.
It may be that it prefers handing out out to readers simply because multiple readers can have the lock at the same time but if a writer gets it, all readers and writers are blocked.
I once had to implement a reader/writer mutex long ago and it had multiple modes depending on what you needed to acheive:
It sounds like that first one is what your system may be doing (I say "may" since it could be that your code is running deterministically despite the threaded nature, ie, the same each time).
If you want to see the difference between fair and non-fair, try the following.
In fair mode, it should be
RWRRRRRRRRRR
. That's because all but the first read lock will wait until the write lock is obtained and released (the write goes first because that's fair).In non-fair mode, you may see
RRRRRRRRRRRW
, the read locks may all be allowed to jump in front of the write lock because they don't interfere with that first read lock, fairness be damned :-)Of course, concepts of fairness may vary depending on the author. It would probably be within the rules for an algorithm to allow reads to sneak in front of writes but with limits. For example, once a write lock is requested, only five more read locks are allowed to sneak through before being queued behind the write lock.
I'm not saying anyone's ever implemented that but it would certainly be a viable option, balancing efficiency against fairness.