Java 如何理解;“不公平”;可重入写回模式?
ReentrantReadWriteLock有一个公平和非公平(默认)模式,但文档对我来说太难理解了 我怎么能理解呢?如果有一些代码示例来演示它,那就太好了 更新Java 如何理解;“不公平”;可重入写回模式?,java,locking,Java,Locking,ReentrantReadWriteLock有一个公平和非公平(默认)模式,但文档对我来说太难理解了 我怎么能理解呢?如果有一些代码示例来演示它,那就太好了 更新 如果我有一个书写线程,和许多阅读线程,哪种模式更适合使用?如果使用非公平模式,写入线程获得锁的可能性是否很小?非公平意味着当新线程准备好获取锁时,锁不会保证获得锁的线程的公平性(假设当时有多个线程请求锁)。换句话说,可以想象,一个线程可能会一直处于饥饿状态,因为其他线程总是设法任意获取锁而不是锁 公平模式的行为更像是先到先得,线程被保
如果我有一个书写线程,和许多阅读线程,哪种模式更适合使用?如果使用非公平模式,写入线程获得锁的可能性是否很小?非公平意味着当新线程准备好获取锁时,锁不会保证获得锁的线程的公平性(假设当时有多个线程请求锁)。换句话说,可以想象,一个线程可能会一直处于饥饿状态,因为其他线程总是设法任意获取锁而不是锁 公平模式的行为更像是先到先得,线程被保证一定程度的公平性,以公平的方式获得锁(例如,在线程开始等待很久之前) 编辑 下面是一个示例程序,演示了锁的公平性(即对公平锁的写锁请求是先到先得的)。比较
FAIR=true
(线程总是按顺序提供)与FAIR=false
(线程有时按顺序提供)时的结果
import java.util.concurrent.locks.ReentrantReadWriteLock;
公共类公平锁定{
公共静态最终布尔公平=真;
私有静态final int NUM_THREADS=3;
私有静态volatile int expectedIndex=0;
公共静态void main(字符串[]args)引发InterruptedException{
ReentrantReadWriteLock.WriteLock lock=new ReentrantReadWriteLock(FAIR.WriteLock();
//我们抓住锁开始,以确保线程在我们准备好之前不会启动
lock.lock();
对于(inti=0;i
编辑(再次)
关于您的更新,使用非公平锁定并不是说线程获得锁定的可能性很低,而是说线程必须等待一段时间的可能性很低
现在,通常随着饥饿期的延长,实际发生时间的概率降低……就像连续10次“正面”掷硬币比连续9次“正面”掷硬币发生的可能性更小
但是如果多个等待线程的选择算法是非随机的,比如“按字母顺序排列的名字总是得到锁”,那么你可能会遇到一个真正的问题,因为随着线程变得越来越饥饿,概率不一定会降低……如果一枚硬币按“头”加权10个连续的头部基本上与9个连续的头部相同
我认为,在非公平锁定的实现中,使用了某种程度上“公平”的硬币。因此,问题实际上变成了公平性(因此,延迟)与吞吐量。使用非公平锁定通常会带来更好的吞吐量,但代价是锁请求的延迟偶尔会出现峰值。哪个更好取决于您自己的需求。当一些线程等待锁时,锁必须选择一个线程才能访问关键部分:
在非公平模式下,它选择没有任何标准的线程。
在公平模式下,它选择等待时间最长的线程
注意:考虑到前面解释的行为仅用于lock()和unlock()方法。由于如果使用锁接口,tryLock()方法不会使线程处于休眠状态,因此fair属性不会影响其功能。如果您能解释一下您不了解的原因,这将很有帮助。源代码可用,但简单地说,已经等待的线程与仅仅尝试获取锁的线程相比,没有任何特权。我想知道线程1是否执行读取操作,而在执行此读取操作时,其他线程2是否执行读取和写入操作。读操作的优先级会高于写操作吗?使用非公平锁定通常会带来稍微好一点的吞吐量,事实上,这要多得多better@best:谢谢,我还没有亲自测试过,所以我不知道真正的结果通常是什么
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class FairLocking {
public static final boolean FAIR = true;
private static final int NUM_THREADS = 3;
private static volatile int expectedIndex = 0;
public static void main(String[] args) throws InterruptedException {
ReentrantReadWriteLock.WriteLock lock = new ReentrantReadWriteLock(FAIR).writeLock();
// we grab the lock to start to make sure the threads don't start until we're ready
lock.lock();
for (int i = 0; i < NUM_THREADS; i++) {
new Thread(new ExampleRunnable(i, lock)).start();
// a cheap way to make sure that runnable 0 requests the first lock
// before runnable 1
Thread.sleep(10);
}
// let the threads go
lock.unlock();
}
private static class ExampleRunnable implements Runnable {
private final int index;
private final ReentrantReadWriteLock.WriteLock writeLock;
public ExampleRunnable(int index, ReentrantReadWriteLock.WriteLock writeLock) {
this.index = index;
this.writeLock = writeLock;
}
public void run() {
while(true) {
writeLock.lock();
try {
// this sleep is a cheap way to make sure the previous thread loops
// around before another thread grabs the lock, does its work,
// loops around and requests the lock again ahead of it.
Thread.sleep(10);
} catch (InterruptedException e) {
//ignored
}
if (index != expectedIndex) {
System.out.printf("Unexpected thread obtained lock! " +
"Expected: %d Actual: %d%n", expectedIndex, index);
System.exit(0);
}
expectedIndex = (expectedIndex+1) % NUM_THREADS;
writeLock.unlock();
}
}
}
}