为什么ReentrantReadWriteLock非公平时写线程拿不到锁?

发布于 2024-12-13 04:32:33 字数 2639 浏览 0 评论 0原文

从这个问题如何理解“非公平”模式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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

待"谢繁草 2024-12-20 04:32:33

非公平只是意味着它不必以排队的方式分发锁(先到先得)。对于如何分发,它不做任何其他保证。事实上,如果需要的话,它可能仍然以排队的方式分发它们。

它可能更喜欢将锁分发给读者,因为多个读者可以同时拥有锁,但如果一个写入者获得了锁,则所有读者和写入者都会被阻止。

很久以前,我曾经不得不实现一个读取器/写入器互斥锁,它有多种模式,具体取决于您需要实现的目标:

  • 更喜欢读取器。
  • 更喜欢作家。
  • 更喜欢替代读者/作者。
  • 排队访问。

听起来第一个是您的系统可能正在做的事情(我说“可能”是因为您的代码可能会确定性地运行,尽管具有线程性质,即每次都相同)。


如果您想了解公平和非公平之间的区别,请尝试以下操作。

  • 让一个线程获得读锁,然后休眠一分钟。
  • 在该睡眠期间,让一个线程请求写入锁并进入睡眠状态一分钟。
  • 然后进行十次尝试的循环来获取和释放读锁。

在公平模式下,它应该是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:

  • prefer reader.
  • prefer writer.
  • prefer alternate reader/writer.
  • queued access.

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.

  • Have one thread get a read lock then go to sleep for a minute.
  • During that sleep, have a thread request a write lock the go to sleep for a minute.
  • Then have a loop of ten attempts to get and release a read lock.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文