JDK-8051848 : ReentrantReadWriteLock.ReadLock fails on unlock by different thread
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util.concurrent
  • Affected Version: 6u26,9
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_7
  • CPU: x86
  • Submitted: 2011-07-07
  • Updated: 2015-11-09
  • Resolved: 2015-09-21
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 9
9 b88Resolved
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_26"
Java(TM) SE Runtime Environment (build 1.6.0_26-b03)
Java HotSpot(TM) Client VM (build 20.1-b02, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7600]

A DESCRIPTION OF THE PROBLEM :
The following code:
                ReentrantReadWriteLock rwl=new ReentrantReadWriteLock();
		final Lock l=rwl.readLock();
		Thread t=new Thread(new Runnable() {
			
			public void run() {
				l.lock();
				
			}
		});
		t.start();
		while (t.isAlive()){
			try {
				Thread.sleep(100);
			} catch (InterruptedException ie){
				// noop
			}
		}
		l.unlock();

Works on Java 1.5, fails on Java 1.6 on the unlock() call with a IllegalStateMonitorException.
The javadoc for Lock.unlock states:
A Lock implementation will usually impose restrictions on which thread can release a lock (typically only the holder of the lock can release it) and may throw an (unchecked) exception if the restriction is violated. Any restrictions and the exception type must be documented by that Lock implementation.
The javadoc for ReentrantReadWriteLock.ReadLock.unlock states:
Attempts to release this lock.
If the number of readers is now zero then the lock is made available for write lock attempts.

Hence, there shouldn't be the limitation that only the thread that took the read lock can unlock it. Since it works in Java 1.5 and I can see in the source the implementation has changed, either it's a code bug or the documentation should be updated.

REGRESSION.  Last worked in version 5.0

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
ReentrantReadWriteLock rwl=new ReentrantReadWriteLock();
		final Lock l=rwl.readLock();
		Thread t=new Thread(new Runnable() {
			
			public void run() {
				l.lock();
				
			}
		});
		t.start();
		while (t.isAlive()){
			try {
				Thread.sleep(100);
			} catch (InterruptedException ie){
				// noop
			}
		}
		l.unlock();

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No error!!
ACTUAL -
IllegalStateMonitorException	at java.util.concurrent.locks.ReentrantReadWriteLock$Sync.tryReleaseShared(ReentrantReadWriteLock.java:363)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.releaseShared(AbstractQueuedSynchronizer.java:1317)
	at java.util.concurrent.locks.ReentrantReadWriteLock$ReadLock.unlock(ReentrantReadWriteLock.java:745)

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.lang.IllegalMonitorStateException
	at java.util.concurrent.locks.ReentrantReadWriteLock$Sync.tryReleaseShared(ReentrantReadWriteLock.java:363)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.releaseShared(AbstractQueuedSynchronizer.java:1317)
	at java.util.concurrent.locks.ReentrantReadWriteLock$ReadLock.unlock(ReentrantReadWriteLock.java:745)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class RRWLTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		ReentrantReadWriteLock rwl=new ReentrantReadWriteLock();
		final Lock l=rwl.readLock();
		Thread t=new Thread(new Runnable() {
			
			public void run() {
				l.lock();
				
			}
		});
		t.start();
		while (t.isAlive()){
			try {
				Thread.sleep(100);
			} catch (InterruptedException ie){
				// noop
			}
		}
		l.unlock();
	}

}
---------- END SOURCE ----------

Comments
As usual with these sorts of changes, they get made in jsr166 CVS first. Either someone can take the initiative and merge it into jdk9 early or (as usual) we do a big merge towards the end of the openjdk release.
09-12-2014

If a javadoc update is needed, it should occur in jdk9 first. I don't see the suggested change in 9 yet.
09-12-2014

Yes, thanks! *** ReentrantReadWriteLock.java.~1.115.~ 2014-08-25 07:08:27.884097883 -0400 --- ReentrantReadWriteLock.java 2014-08-25 07:51:17.709853349 -0400 *************** *** 849,854 **** --- 849,857 ---- * is made available for write lock attempts. If the current * thread does not hold this lock then {@link * IllegalMonitorStateException} is thrown. + * + * @throws IllegalMonitorStateException If the current thread + * does not hold this lock */ public void unlock() { sync.releaseShared(1);
25-08-2014

@throws IllegalMonitorStateException If the current thread does not hold this lock :)
25-08-2014

The JDK5 documentation allows the JDK6+ behavior. But I agree that it should be more helpful. In fact, considering that the JDK6+ behavior cannot at this point be reverted without impacting compatibility, the best change is just to add a sentence in RRWL.ReadLock.unlock. This is now committed in jsr166 CVS: *** ReentrantReadWriteLock.java.~1.114.~ 2014-05-25 06:53:14.867328099 -0400 --- ReentrantReadWriteLock.java 2014-08-25 07:08:27.884097883 -0400 *************** *** 846,852 **** * Attempts to release this lock. * * <p>If the number of readers is now zero then the lock ! * is made available for write lock attempts. */ public void unlock() { sync.releaseShared(1); --- 846,854 ---- * Attempts to release this lock. * * <p>If the number of readers is now zero then the lock ! * is made available for write lock attempts. If the current ! * thread does not hold this lock then {@link ! * IllegalMonitorStateException} is thrown. */ public void unlock() { sync.releaseShared(1);
25-08-2014

Yes the documentation should be updated with an implemenation note. JDK 6 changed the way lock "ownership" of the read-lock was managed.
24-07-2014

Tested with 8u20 and is reproduce able (not tested with JDK5.0 though) : $ /java/re/jdk/8u20/promoted/latest/binaries/solaris-sparcv9/bin/java RRWLTest Exception in thread "main" java.lang.IllegalMonitorStateException: attempt to unlock read lock, not locked by current thread at java.util.concurrent.locks.ReentrantReadWriteLock$Sync.unmatchedUnlockException(ReentrantReadWriteLock.java:444) at java.util.concurrent.locks.ReentrantReadWriteLock$Sync.tryReleaseShared(ReentrantReadWriteLock.java:428) at java.util.concurrent.locks.AbstractQueuedSynchronizer.releaseShared(AbstractQueuedSynchronizer.java:1341) at java.util.concurrent.locks.ReentrantReadWriteLock$ReadLock.unlock(ReentrantReadWriteLock.java:881) at RRWLTest.main(RRWLTest.java:27)
24-07-2014