Java 如何理解;“不公平”;可重入写回模式?

Java 如何理解;“不公平”;可重入写回模式?,java,locking,Java,Locking,ReentrantReadWriteLock有一个公平和非公平(默认)模式,但文档对我来说太难理解了 我怎么能理解呢?如果有一些代码示例来演示它,那就太好了 更新 如果我有一个书写线程,和许多阅读线程,哪种模式更适合使用?如果使用非公平模式,写入线程获得锁的可能性是否很小?非公平意味着当新线程准备好获取锁时,锁不会保证获得锁的线程的公平性(假设当时有多个线程请求锁)。换句话说,可以想象,一个线程可能会一直处于饥饿状态,因为其他线程总是设法任意获取锁而不是锁 公平模式的行为更像是先到先得,线程被保

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