JDK-6816565 : ReentrantReadWriteLock readLock behavior doesn't comply the java api doc
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.concurrent
  • Affected Version: 6u12,6u16
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: linux,solaris_2.5.1
  • CPU: x86
  • Submitted: 2009-03-12
  • Updated: 2011-02-16
  • Resolved: 2009-05-15
Related Reports
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_12"
Java(TM) SE Runtime Environment (build 1.6.0_12-b04)
Java HotSpot(TM) Server VM (build 11.2-b01, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
Linux Freeman-pc 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 :
java version "1.6.0_12"
Java(TM) SE Runtime Environment (build 1.6.0_12-b04)
Java HotSpot(TM) Server VM (build 11.2-b01, mixed mode)
we cannot get a read lock if the sequence is
 * thread 1:  rwlock.readLock().lock(); //should sucess to get the readLock
 * thread 2:  rwlock.writeLock().lock();//should fail to get the writeLock since the readLock already hold by another thread
 * thread 3: rwlock.readLock().lock(); //should success to get the readLock since no other thread hold the writeLock but failed only with JDK6, (OK with JDK 5)

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
run the code like
import java.util.concurrent.locks.*;
import java.util.concurrent.*;

/**
 * Test ReentrantReadWrite Lock.
 * This code shows how we cannot get a read lock if the sequence is
 * thread 1:  rwlock.readLock().lock(); //should sucess to get the readLock
 * thread 2:  rwlock.writeLock().lock();//should fail to get the writeLock since the readLock already hold by another thread
 * thread 3: rwlock.readLock().lock(); //should success to get the readLock since no other thread hold the writeLock but failed only with JDK6, (OK with JDK 5)
 */
public class TestReadWriteLock {

    void log(String s) {
        System.out.printf("%s: %s%n", Thread.currentThread().getName(), s);
    }


    public static void main(String[] args) throws Exception {

        new TestReadWriteLock().runTest();
    }

    public void runTest() throws Exception {
        ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock(false);

        // obtain read lock
        rwlock.readLock().lock(); //should sucess to get the readLock

        // start 1 threads
        for (int i = 0; i < 1; i++) {
            new Thread(this.new WriteLockThread(rwlock), "TryWrite" + i).start();
            Thread.sleep(3000);
            new Thread(this.new ReadLockThread(rwlock), "TryRead" + i).start();
        }
    }

    class WriteLockThread implements Runnable {

        private ReentrantReadWriteLock rwlock;

        public WriteLockThread(ReentrantReadWriteLock rwlock) {
            this.rwlock = rwlock;
        }
        public void run() {
            try {
                log("try get writelock");
                rwlock.writeLock().lock(); //should fail to get the writeLock since the readLock already hold by another thread
                log("can get writelock"); // can't print out
            } finally {
            }

        }

    }

    class ReadLockThread implements Runnable {

        private ReentrantReadWriteLock rwlock;

        public ReadLockThread(ReentrantReadWriteLock rwlock) {
            this.rwlock = rwlock;
        }
        public void run() {
            try {
                log("try get readlock");
                rwlock.readLock().lock();//should success to get the readLock since no other thread hold the writeLock but failed only with JDK6, (OK with JDK 5)
                log("can get readlock");//get this print out with JDK5 as expected, but not with JDK6, which means the error in JDK6
            } finally {
                log("unlock readlock");
                rwlock.readLock().unlock();
            }

        }

    }
}


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
TryWrite0: try get writelock
TryRead0: try get readlock
TryRead0: can get readlock
TryRead0: unlock readlock
ACTUAL -
TryWrite0: try get writelock
TryRead0: try get readlock

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.util.concurrent.locks.*;
import java.util.concurrent.*;

/**
 * Test ReentrantReadWrite Lock.
 * This code shows how we cannot get a read lock if the sequence is
 * thread 1:  rwlock.readLock().lock(); //should sucess to get the readLock
 * thread 2:  rwlock.writeLock().lock();//should fail to get the writeLock since the readLock already hold by another thread
 * thread 3: rwlock.readLock().lock(); //should success to get the readLock since no other thread hold the writeLock but failed only with JDK6, (OK with JDK 5)
 */
public class TestReadWriteLock {

    void log(String s) {
        System.out.printf("%s: %s%n", Thread.currentThread().getName(), s);
    }


    public static void main(String[] args) throws Exception {

        new TestReadWriteLock().runTest();
    }

    public void runTest() throws Exception {
        ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock(false);

        // obtain read lock
        rwlock.readLock().lock(); //should sucess to get the readLock

        // start 1 threads
        for (int i = 0; i < 1; i++) {
            new Thread(this.new WriteLockThread(rwlock), "TryWrite" + i).start();
            Thread.sleep(3000);
            new Thread(this.new ReadLockThread(rwlock), "TryRead" + i).start();
        }
    }

    class WriteLockThread implements Runnable {

        private ReentrantReadWriteLock rwlock;

        public WriteLockThread(ReentrantReadWriteLock rwlock) {
            this.rwlock = rwlock;
        }
        public void run() {
            try {
                log("try get writelock");
                rwlock.writeLock().lock(); //should fail to get the writeLock since the readLock already hold by another thread
                log("can get writelock"); // can't print out
            } finally {
            }

        }

    }

    class ReadLockThread implements Runnable {

        private ReentrantReadWriteLock rwlock;

        public ReadLockThread(ReentrantReadWriteLock rwlock) {
            this.rwlock = rwlock;
        }
        public void run() {
            try {
                log("try get readlock");
                rwlock.readLock().lock();//should success to get the readLock since no other thread hold the writeLock but failed only with JDK6, (OK with JDK 5)
                log("can get readlock");//get this print out with JDK5 as expected, but not with JDK6, which means the error in JDK6
            } finally {
                log("unlock readlock");
                rwlock.readLock().unlock();
            }

        }

    }
}

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

Comments
EVALUATION 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.
15-05-2009

PUBLIC COMMENTS This CR was overlooked and will be evaluated ASAP - apologies for the delay.
12-05-2009