JDK-6893626 : ReadWriteLock: writeLock request blocks future readLock despite policy unfair
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.concurrent
  • Affected Version: 6u16
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: solaris_2.5.1
  • CPU: x86
  • Submitted: 2009-10-21
  • Updated: 2011-02-16
  • Resolved: 2009-10-22
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_16"
Java(TM) SE Runtime Environment (build 1.6.0_16-b01)
Java HotSpot(TM) Server VM (build 14.2-b01, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Linux boba-a 2.6.9-42.ELsmp #1 SMP Wed Jul 12 23:27:17 EDT 2006 i686 i686 i386 GNU/Linux


A DESCRIPTION OF THE PROBLEM :
The problem can be reproduced with 3 concurrent threads sharing a ReadWriteLock, 2 blocking it for reading, the third one for writing.
What I observe is that once the Writing thread has requested the lock and is waiting that a read lock is released, no other thread can take the read lock.

Actually in the test case below the first thread ("Mainthread") that has taken the lock first can take again the lock after the write lock request, but another thread cannot take it ("ReadThread") before that the writing thread ("WriteThread") takes it before.

This behaviour could not be compliant with the "unfair" policy which is the default one of the ReadWriteLock (but normal with policy "fair"), since a thread should be able take the lock whenever it's possible.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
See test case provided.


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
MainThread - lock
MainThread - lock
MainThread - unlock
                                                                            WriteThread - lock
                                      ReadThread - lock
                                      ReadThread - unlock
MainThread - lock
MainThread - unlock
MainThread - unlock
                                                                            WriteThread - unlock

ACTUAL -
MainThread - lock
MainThread - lock
MainThread - unlock
                                                                            WriteThread - lock
                                      ReadThread - lock
MainThread - lock
MainThread - unlock
MainThread - unlock
                                                                            WriteThread - unlock
                                      ReadThread - unlock


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
// Code to reproduce
import java.util.concurrent.locks.*;
import java.util.concurrent.*;

/**
 * Test ReentrantReadWrite Lock.
 * This code shows how we cannot regain a read lock if a write lock times out.
 */

public class TestWriteInRead {

    public void doTest() {
        ReentrantReadWriteLock lock = new ReentrantReadWriteLock(false); // unfair!
        // obtain read lock
        System.out.println("MainThread - lock");
        lock.readLock().lock();

        System.out.println("MainThread - lock");
        lock.readLock().lock();
        System.out.println("MainThread - unlock");
        lock.readLock().unlock();

        // start Writer and wait that Writer takes the lock
        new Thread(new WriteThread(lock)).start();
        try {
            Thread.sleep(1000);
        }catch(InterruptedException e) {
            e.printStackTrace();
        }

        // start Reader and wait that Reader takes the lock
        new Thread(new ReadThread(lock)).start();
        try {
            Thread.sleep(1000);
        }catch(InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("MainThread - lock");
        lock.readLock().lock();
        System.out.println("MainThread - unlock");
        lock.readLock().unlock();

        System.out.println("MainThread - unlock");
        lock.readLock().unlock();
    }

    public static void main(String[] args) {

        TestWriteInRead tester = new TestWriteInRead();
        tester.doTest();
    }

    class ReadThread extends Thread {

        public void run() {
            System.out.println("                         ReadThread - lock");
            lock.readLock().lock();

            System.out.println("                         ReadThread - unlock");
            lock.readLock().unlock();
        }

        public ReadThread (ReadWriteLock lock) {
            this.lock = lock;
        }

        ReadWriteLock lock;
    }

    class WriteThread extends Thread {

        public void run() {
            System.out.println("                                                  WriteThread - lock");
            lock.writeLock().lock();

            System.out.println("                                                  WriteThread - unlock");
            lock.writeLock().unlock();
        }

        public WriteThread (ReadWriteLock lock) {
            this.lock = lock;
        }

        ReadWriteLock lock;
    }
}

---------- END SOURCE ----------

Comments
EVALUATION Reproduced from 6816565: The submitter states that the observed behaviour does not comply with the Java API doc, but makes no reference to what part of the doc is not being complied with. The documentation for JDK 6 states: * Acquisition order This class does not impose a reader or writer preference ordering for lock access. However, it does support an optional fairness policy. Non-fair mode (default) When constructed as non-fair (the default), the order of entry to the read and write lock is unspecified, subject to reentrancy constraints. A nonfair lock that is continously contended may indefinitely postpone one or more reader or writer threads, but will normally have higher throughput than a fair lock. ---- In short, there is no specified ordering behaviour for non-Fair mode. In practice what the implementation does, to prevent indefinite postponement, is give preference to a waiting writer if the writer is head of the queue - as is the case in this example code. This is not a true "writer preference" policy as the preference only exists when readers are active, but not queued, a writer is queued, then a new reader arrives. If a reader arrives while other readers are still being dequeued, then it will proceed ahead of all waiting writers. Both the specification of ReentrantReadWriteLock and the implementation have been modified between Java 5 and Java 6 - hence the difference in observed behaviour. In Java 5 the documentation overstated the behaviour for non-Fair - see Bug 6281487. ---
22-10-2009