1
votes

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".

1
FYI - I seem to see this "first-come-first-served" behavior when testing on my Linux box, but when we stuck the same code onto a Windows machine we did see the starvation where the same thread appeared to keep getting the lock the majority of the time. So as the JavaDoc (and @paxdiablo) said "fairness" is a relative guarantee, whereas "non-fairness" is not a guarantee (may be fair, may not).Shadow Man
PS - My Linux box had 4 hyper-threaded cores (8 simultaneous threads) whereas the Windows box had 2 virtual cores (2 simultaneous threads). So this may have something to do with it since I likely never had more than 8 simultaneous lock attempts in my Linux testing but we certainly had more than 2 in the Windows environment.Shadow Man

1 Answers

4
votes

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.