8159924: Various improvements to StampedLock code
authordl
Fri, 15 Jul 2016 13:51:43 -0700
changeset 39722 a0b0fa5763b1
parent 39678 5bf88dce615f
child 39723 9aa34e4a0469
8159924: Various improvements to StampedLock code Reviewed-by: martin, psandoz, rriggs, plevart, dfuchs
jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java
jdk/test/java/util/concurrent/tck/StampedLockTest.java
--- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java	Wed Jul 05 21:58:29 2017 +0200
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java	Fri Jul 15 13:51:43 2016 -0700
@@ -35,7 +35,10 @@
 
 package java.util.concurrent.locks;
 
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
 import java.util.concurrent.TimeUnit;
+import jdk.internal.vm.annotation.ReservedStackAccess;
 
 /**
  * A capability-based lock with three modes for controlling read/write
@@ -108,6 +111,10 @@
  * into initial unlocked state, so they are not useful for remote
  * locking.
  *
+ * <p>Like {@link java.util.concurrent.Semaphore Semaphore}, but unlike most
+ * {@link Lock} implementations, StampedLocks have no notion of ownership.
+ * Locks acquired in one thread can be released or converted in another.
+ *
  * <p>The scheduling policy of StampedLock does not consistently
  * prefer readers over writers or vice versa.  All "try" methods are
  * best-effort and do not necessarily conform to any scheduling or
@@ -126,7 +133,7 @@
  * in a class that maintains simple two-dimensional points. The sample
  * code illustrates some try/catch conventions even though they are
  * not strictly needed here because no exceptions can occur in their
- * bodies.<br>
+ * bodies.
  *
  * <pre> {@code
  * class Point {
@@ -234,9 +241,7 @@
      * used in the acquire methods to reduce (increasingly expensive)
      * context switching while also avoiding sustained memory
      * thrashing among many threads.  We limit spins to the head of
-     * queue. A thread spin-waits up to SPINS times (where each
-     * iteration decreases spin count with 50% probability) before
-     * blocking. If, upon wakening it fails to obtain lock, and is
+     * queue. If, upon wakening, a thread fails to obtain lock, and is
      * still (or becomes) the first waiting thread (which indicates
      * that some other thread barged and obtained lock), it escalates
      * spins (up to MAX_HEAD_SPINS) to reduce the likelihood of
@@ -252,7 +257,7 @@
      * to normal volatile reads (of "state").  To force orderings of
      * reads before a validation and the validation itself in those
      * cases where this is not already forced, we use
-     * Unsafe.loadFence.
+     * VarHandle.acquireFence.
      *
      * The memory layout keeps lock state and queue pointers together
      * (normally on the same cache line). This usually works well for
@@ -290,7 +295,20 @@
     private static final long ABITS = RBITS | WBIT;
     private static final long SBITS = ~RBITS; // note overlap with ABITS
 
-    // Initial value for lock state; avoid failure value zero
+    /*
+     * 3 stamp modes can be distinguished by examining (m = stamp & ABITS):
+     * write mode: m == WBIT
+     * optimistic read mode: m == 0L (even when read lock is held)
+     * read mode: m > 0L && m <= RFULL (the stamp is a copy of state, but the
+     * read hold count in the stamp is unused other than to determine mode)
+     *
+     * This differs slightly from the encoding of state:
+     * (state & ABITS) == 0L indicates the lock is currently unlocked.
+     * (state & ABITS) == RBITS is a special transient value
+     * indicating spin-locked to manipulate reader bits overflow.
+     */
+
+    /** Initial value for lock state; avoids failure value zero. */
     private static final long ORIGIN = WBIT << 1;
 
     // Special value from cancelled acquire methods so caller can throw IE
@@ -341,25 +359,27 @@
      * Exclusively acquires the lock, blocking if necessary
      * until available.
      *
-     * @return a stamp that can be used to unlock or convert mode
+     * @return a write stamp that can be used to unlock or convert mode
      */
+    @ReservedStackAccess
     public long writeLock() {
         long s, next;  // bypass acquireWrite in fully unlocked case only
         return ((((s = state) & ABITS) == 0L &&
-                 U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
+                 STATE.compareAndSet(this, s, next = s + WBIT)) ?
                 next : acquireWrite(false, 0L));
     }
 
     /**
      * Exclusively acquires the lock if it is immediately available.
      *
-     * @return a stamp that can be used to unlock or convert mode,
+     * @return a write stamp that can be used to unlock or convert mode,
      * or zero if the lock is not available
      */
+    @ReservedStackAccess
     public long tryWriteLock() {
         long s, next;
         return ((((s = state) & ABITS) == 0L &&
-                 U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
+                 STATE.compareAndSet(this, s, next = s + WBIT)) ?
                 next : 0L);
     }
 
@@ -371,7 +391,7 @@
      *
      * @param time the maximum time to wait for the lock
      * @param unit the time unit of the {@code time} argument
-     * @return a stamp that can be used to unlock or convert mode,
+     * @return a write stamp that can be used to unlock or convert mode,
      * or zero if the lock is not available
      * @throws InterruptedException if the current thread is interrupted
      * before acquiring the lock
@@ -399,10 +419,11 @@
      * Behavior under interruption matches that specified
      * for method {@link Lock#lockInterruptibly()}.
      *
-     * @return a stamp that can be used to unlock or convert mode
+     * @return a write stamp that can be used to unlock or convert mode
      * @throws InterruptedException if the current thread is interrupted
      * before acquiring the lock
      */
+    @ReservedStackAccess
     public long writeLockInterruptibly() throws InterruptedException {
         long next;
         if (!Thread.interrupted() &&
@@ -415,33 +436,34 @@
      * Non-exclusively acquires the lock, blocking if necessary
      * until available.
      *
-     * @return a stamp that can be used to unlock or convert mode
+     * @return a read stamp that can be used to unlock or convert mode
      */
+    @ReservedStackAccess
     public long readLock() {
         long s = state, next;  // bypass acquireRead on common uncontended case
         return ((whead == wtail && (s & ABITS) < RFULL &&
-                 U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ?
+                 STATE.compareAndSet(this, s, next = s + RUNIT)) ?
                 next : acquireRead(false, 0L));
     }
 
     /**
      * Non-exclusively acquires the lock if it is immediately available.
      *
-     * @return a stamp that can be used to unlock or convert mode,
+     * @return a read stamp that can be used to unlock or convert mode,
      * or zero if the lock is not available
      */
+    @ReservedStackAccess
     public long tryReadLock() {
-        for (;;) {
-            long s, m, next;
-            if ((m = (s = state) & ABITS) == WBIT)
-                return 0L;
-            else if (m < RFULL) {
-                if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
+        long s, m, next;
+        while ((m = (s = state) & ABITS) != WBIT) {
+            if (m < RFULL) {
+                if (STATE.compareAndSet(this, s, next = s + RUNIT))
                     return next;
             }
             else if ((next = tryIncReaderOverflow(s)) != 0L)
                 return next;
         }
+        return 0L;
     }
 
     /**
@@ -452,11 +474,12 @@
      *
      * @param time the maximum time to wait for the lock
      * @param unit the time unit of the {@code time} argument
-     * @return a stamp that can be used to unlock or convert mode,
+     * @return a read stamp that can be used to unlock or convert mode,
      * or zero if the lock is not available
      * @throws InterruptedException if the current thread is interrupted
      * before acquiring the lock
      */
+    @ReservedStackAccess
     public long tryReadLock(long time, TimeUnit unit)
         throws InterruptedException {
         long s, m, next, deadline;
@@ -464,7 +487,7 @@
         if (!Thread.interrupted()) {
             if ((m = (s = state) & ABITS) != WBIT) {
                 if (m < RFULL) {
-                    if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
+                    if (STATE.compareAndSet(this, s, next = s + RUNIT))
                         return next;
                 }
                 else if ((next = tryIncReaderOverflow(s)) != 0L)
@@ -486,10 +509,11 @@
      * Behavior under interruption matches that specified
      * for method {@link Lock#lockInterruptibly()}.
      *
-     * @return a stamp that can be used to unlock or convert mode
+     * @return a read stamp that can be used to unlock or convert mode
      * @throws InterruptedException if the current thread is interrupted
      * before acquiring the lock
      */
+    @ReservedStackAccess
     public long readLockInterruptibly() throws InterruptedException {
         long next;
         if (!Thread.interrupted() &&
@@ -502,7 +526,7 @@
      * Returns a stamp that can later be validated, or zero
      * if exclusively locked.
      *
-     * @return a stamp, or zero if exclusively locked
+     * @return a valid optimistic read stamp, or zero if exclusively locked
      */
     public long tryOptimisticRead() {
         long s;
@@ -522,11 +546,29 @@
      * since issuance of the given stamp; else false
      */
     public boolean validate(long stamp) {
-        U.loadFence();
+        VarHandle.acquireFence();
         return (stamp & SBITS) == (state & SBITS);
     }
 
     /**
+     * Returns an unlocked state, incrementing the version and
+     * avoiding special failure value 0L.
+     *
+     * @param s a write-locked state (or stamp)
+     */
+    private static long unlockWriteState(long s) {
+        return ((s += WBIT) == 0L) ? ORIGIN : s;
+    }
+
+    private long unlockWriteInternal(long s) {
+        long next; WNode h;
+        STATE.setVolatile(this, next = unlockWriteState(s));
+        if ((h = whead) != null && h.status != 0)
+            release(h);
+        return next;
+    }
+
+    /**
      * If the lock state matches the given stamp, releases the
      * exclusive lock.
      *
@@ -534,13 +576,11 @@
      * @throws IllegalMonitorStateException if the stamp does
      * not match the current state of this lock
      */
+    @ReservedStackAccess
     public void unlockWrite(long stamp) {
-        WNode h;
         if (state != stamp || (stamp & WBIT) == 0L)
             throw new IllegalMonitorStateException();
-        U.putLongVolatile(this, STATE, (stamp += WBIT) == 0L ? ORIGIN : stamp);
-        if ((h = whead) != null && h.status != 0)
-            release(h);
+        unlockWriteInternal(stamp);
     }
 
     /**
@@ -551,22 +591,23 @@
      * @throws IllegalMonitorStateException if the stamp does
      * not match the current state of this lock
      */
+    @ReservedStackAccess
     public void unlockRead(long stamp) {
         long s, m; WNode h;
-        for (;;) {
-            if (((s = state) & SBITS) != (stamp & SBITS) ||
-                (stamp & ABITS) == 0L || (m = s & ABITS) == 0L || m == WBIT)
-                throw new IllegalMonitorStateException();
+        while (((s = state) & SBITS) == (stamp & SBITS)
+               && (stamp & RBITS) > 0L
+               && ((m = s & RBITS) > 0L)) {
             if (m < RFULL) {
-                if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
+                if (STATE.compareAndSet(this, s, s - RUNIT)) {
                     if (m == RUNIT && (h = whead) != null && h.status != 0)
                         release(h);
-                    break;
+                    return;
                 }
             }
             else if (tryDecReaderOverflow(s) != 0L)
-                break;
+                return;
         }
+        throw new IllegalMonitorStateException();
     }
 
     /**
@@ -577,32 +618,12 @@
      * @throws IllegalMonitorStateException if the stamp does
      * not match the current state of this lock
      */
+    @ReservedStackAccess
     public void unlock(long stamp) {
-        long a = stamp & ABITS, m, s; WNode h;
-        while (((s = state) & SBITS) == (stamp & SBITS)) {
-            if ((m = s & ABITS) == 0L)
-                break;
-            else if (m == WBIT) {
-                if (a != m)
-                    break;
-                U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s);
-                if ((h = whead) != null && h.status != 0)
-                    release(h);
-                return;
-            }
-            else if (a == 0L || a >= WBIT)
-                break;
-            else if (m < RFULL) {
-                if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
-                    if (m == RUNIT && (h = whead) != null && h.status != 0)
-                        release(h);
-                    return;
-                }
-            }
-            else if (tryDecReaderOverflow(s) != 0L)
-                return;
-        }
-        throw new IllegalMonitorStateException();
+        if ((stamp & WBIT) != 0)
+            unlockWrite(stamp);
+        else
+            unlockRead(stamp);
     }
 
     /**
@@ -623,7 +644,7 @@
             if ((m = s & ABITS) == 0L) {
                 if (a != 0L)
                     break;
-                if (U.compareAndSwapLong(this, STATE, s, next = s + WBIT))
+                if (STATE.compareAndSet(this, s, next = s + WBIT))
                     return next;
             }
             else if (m == WBIT) {
@@ -632,8 +653,7 @@
                 return stamp;
             }
             else if (m == RUNIT && a != 0L) {
-                if (U.compareAndSwapLong(this, STATE, s,
-                                         next = s - RUNIT + WBIT))
+                if (STATE.compareAndSet(this, s, next = s - RUNIT + WBIT))
                     return next;
             }
             else
@@ -654,30 +674,32 @@
      * @return a valid read stamp, or zero on failure
      */
     public long tryConvertToReadLock(long stamp) {
-        long a = stamp & ABITS, m, s, next; WNode h;
+        long a, s, next; WNode h;
         while (((s = state) & SBITS) == (stamp & SBITS)) {
-            if ((m = s & ABITS) == 0L) {
-                if (a != 0L)
+            if ((a = stamp & ABITS) >= WBIT) {
+                // write stamp
+                if (s != stamp)
                     break;
-                else if (m < RFULL) {
-                    if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
+                STATE.setVolatile(this, next = unlockWriteState(s) + RUNIT);
+                if ((h = whead) != null && h.status != 0)
+                    release(h);
+                return next;
+            }
+            else if (a == 0L) {
+                // optimistic read stamp
+                if ((s & ABITS) < RFULL) {
+                    if (STATE.compareAndSet(this, s, next = s + RUNIT))
                         return next;
                 }
                 else if ((next = tryIncReaderOverflow(s)) != 0L)
                     return next;
             }
-            else if (m == WBIT) {
-                if (a != m)
+            else {
+                // already a read stamp
+                if ((s & ABITS) == 0L)
                     break;
-                U.putLongVolatile(this, STATE, next = s + (WBIT + RUNIT));
-                if ((h = whead) != null && h.status != 0)
-                    release(h);
-                return next;
+                return stamp;
             }
-            else if (a != 0L && a < WBIT)
-                return stamp;
-            else
-                break;
         }
         return 0L;
     }
@@ -693,29 +715,22 @@
      * @return a valid optimistic read stamp, or zero on failure
      */
     public long tryConvertToOptimisticRead(long stamp) {
-        long a = stamp & ABITS, m, s, next; WNode h;
-        U.loadFence();
-        for (;;) {
-            if (((s = state) & SBITS) != (stamp & SBITS))
-                break;
-            if ((m = s & ABITS) == 0L) {
-                if (a != 0L)
+        long a, m, s, next; WNode h;
+        VarHandle.acquireFence();
+        while (((s = state) & SBITS) == (stamp & SBITS)) {
+            if ((a = stamp & ABITS) >= WBIT) {
+                // write stamp
+                if (s != stamp)
                     break;
-                return s;
+                return unlockWriteInternal(s);
             }
-            else if (m == WBIT) {
-                if (a != m)
-                    break;
-                U.putLongVolatile(this, STATE,
-                                  next = (s += WBIT) == 0L ? ORIGIN : s);
-                if ((h = whead) != null && h.status != 0)
-                    release(h);
-                return next;
-            }
-            else if (a == 0L || a >= WBIT)
+            else if (a == 0L)
+                // already an optimistic read stamp
+                return stamp;
+            else if ((m = s & ABITS) == 0L) // invalid read stamp
                 break;
             else if (m < RFULL) {
-                if (U.compareAndSwapLong(this, STATE, s, next = s - RUNIT)) {
+                if (STATE.compareAndSet(this, s, next = s - RUNIT)) {
                     if (m == RUNIT && (h = whead) != null && h.status != 0)
                         release(h);
                     return next & SBITS;
@@ -734,12 +749,11 @@
      *
      * @return {@code true} if the lock was held, else false
      */
+    @ReservedStackAccess
     public boolean tryUnlockWrite() {
-        long s; WNode h;
+        long s;
         if (((s = state) & WBIT) != 0L) {
-            U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s);
-            if ((h = whead) != null && h.status != 0)
-                release(h);
+            unlockWriteInternal(s);
             return true;
         }
         return false;
@@ -752,11 +766,12 @@
      *
      * @return {@code true} if the read lock was held, else false
      */
+    @ReservedStackAccess
     public boolean tryUnlockRead() {
         long s, m; WNode h;
         while ((m = (s = state) & ABITS) != 0L && m < WBIT) {
             if (m < RFULL) {
-                if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
+                if (STATE.compareAndSet(this, s, s - RUNIT)) {
                     if (m == RUNIT && (h = whead) != null && h.status != 0)
                         release(h);
                     return true;
@@ -832,32 +847,30 @@
      * Returns a plain {@link Lock} view of this StampedLock in which
      * the {@link Lock#lock} method is mapped to {@link #readLock},
      * and similarly for other methods. The returned Lock does not
-     * support a {@link Condition}; method {@link
-     * Lock#newCondition()} throws {@code
-     * UnsupportedOperationException}.
+     * support a {@link Condition}; method {@link Lock#newCondition()}
+     * throws {@code UnsupportedOperationException}.
      *
      * @return the lock
      */
     public Lock asReadLock() {
         ReadLockView v;
-        return ((v = readLockView) != null ? v :
-                (readLockView = new ReadLockView()));
+        if ((v = readLockView) != null) return v;
+        return readLockView = new ReadLockView();
     }
 
     /**
      * Returns a plain {@link Lock} view of this StampedLock in which
      * the {@link Lock#lock} method is mapped to {@link #writeLock},
      * and similarly for other methods. The returned Lock does not
-     * support a {@link Condition}; method {@link
-     * Lock#newCondition()} throws {@code
-     * UnsupportedOperationException}.
+     * support a {@link Condition}; method {@link Lock#newCondition()}
+     * throws {@code UnsupportedOperationException}.
      *
      * @return the lock
      */
     public Lock asWriteLock() {
         WriteLockView v;
-        return ((v = writeLockView) != null ? v :
-                (writeLockView = new WriteLockView()));
+        if ((v = writeLockView) != null) return v;
+        return writeLockView = new WriteLockView();
     }
 
     /**
@@ -870,8 +883,8 @@
      */
     public ReadWriteLock asReadWriteLock() {
         ReadWriteLockView v;
-        return ((v = readWriteLockView) != null ? v :
-                (readWriteLockView = new ReadWriteLockView()));
+        if ((v = readWriteLockView) != null) return v;
+        return readWriteLockView = new ReadWriteLockView();
     }
 
     // view classes
@@ -917,35 +930,32 @@
     // Needed because view-class lock methods throw away stamps.
 
     final void unstampedUnlockWrite() {
-        WNode h; long s;
+        long s;
         if (((s = state) & WBIT) == 0L)
             throw new IllegalMonitorStateException();
-        U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s);
-        if ((h = whead) != null && h.status != 0)
-            release(h);
+        unlockWriteInternal(s);
     }
 
     final void unstampedUnlockRead() {
-        for (;;) {
-            long s, m; WNode h;
-            if ((m = (s = state) & ABITS) == 0L || m >= WBIT)
-                throw new IllegalMonitorStateException();
-            else if (m < RFULL) {
-                if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
+        long s, m; WNode h;
+        while ((m = (s = state) & RBITS) > 0L) {
+            if (m < RFULL) {
+                if (STATE.compareAndSet(this, s, s - RUNIT)) {
                     if (m == RUNIT && (h = whead) != null && h.status != 0)
                         release(h);
-                    break;
+                    return;
                 }
             }
             else if (tryDecReaderOverflow(s) != 0L)
-                break;
+                return;
         }
+        throw new IllegalMonitorStateException();
     }
 
     private void readObject(java.io.ObjectInputStream s)
         throws java.io.IOException, ClassNotFoundException {
         s.defaultReadObject();
-        U.putLongVolatile(this, STATE, ORIGIN); // reset to unlocked state
+        STATE.setVolatile(this, ORIGIN); // reset to unlocked state
     }
 
     // internals
@@ -961,15 +971,16 @@
     private long tryIncReaderOverflow(long s) {
         // assert (s & ABITS) >= RFULL;
         if ((s & ABITS) == RFULL) {
-            if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
+            if (STATE.compareAndSet(this, s, s | RBITS)) {
                 ++readerOverflow;
-                U.putLongVolatile(this, STATE, s);
+                STATE.setVolatile(this, s);
                 return s;
             }
         }
-        else if ((LockSupport.nextSecondarySeed() &
-                  OVERFLOW_YIELD_RATE) == 0)
+        else if ((LockSupport.nextSecondarySeed() & OVERFLOW_YIELD_RATE) == 0)
             Thread.yield();
+        else
+            Thread.onSpinWait();
         return 0L;
     }
 
@@ -982,7 +993,7 @@
     private long tryDecReaderOverflow(long s) {
         // assert (s & ABITS) >= RFULL;
         if ((s & ABITS) == RFULL) {
-            if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
+            if (STATE.compareAndSet(this, s, s | RBITS)) {
                 int r; long next;
                 if ((r = readerOverflow) > 0) {
                     readerOverflow = r - 1;
@@ -990,13 +1001,14 @@
                 }
                 else
                     next = s - RUNIT;
-                U.putLongVolatile(this, STATE, next);
+                STATE.setVolatile(this, next);
                 return next;
             }
         }
-        else if ((LockSupport.nextSecondarySeed() &
-                  OVERFLOW_YIELD_RATE) == 0)
+        else if ((LockSupport.nextSecondarySeed() & OVERFLOW_YIELD_RATE) == 0)
             Thread.yield();
+        else
+            Thread.onSpinWait();
         return 0L;
     }
 
@@ -1010,14 +1022,14 @@
     private void release(WNode h) {
         if (h != null) {
             WNode q; Thread w;
-            U.compareAndSwapInt(h, WSTATUS, WAITING, 0);
+            WSTATUS.compareAndSet(h, WAITING, 0);
             if ((q = h.next) == null || q.status == CANCELLED) {
                 for (WNode t = wtail; t != null && t != h; t = t.prev)
                     if (t.status <= 0)
                         q = t;
             }
             if (q != null && (w = q.thread) != null)
-                U.unpark(w);
+                LockSupport.unpark(w);
         }
     }
 
@@ -1035,25 +1047,25 @@
         for (int spins = -1;;) { // spin while enqueuing
             long m, s, ns;
             if ((m = (s = state) & ABITS) == 0L) {
-                if (U.compareAndSwapLong(this, STATE, s, ns = s + WBIT))
+                if (STATE.compareAndSet(this, s, ns = s + WBIT))
                     return ns;
             }
             else if (spins < 0)
                 spins = (m == WBIT && wtail == whead) ? SPINS : 0;
             else if (spins > 0) {
-                if (LockSupport.nextSecondarySeed() >= 0)
-                    --spins;
+                --spins;
+                Thread.onSpinWait();
             }
             else if ((p = wtail) == null) { // initialize queue
                 WNode hd = new WNode(WMODE, null);
-                if (U.compareAndSwapObject(this, WHEAD, null, hd))
+                if (WHEAD.weakCompareAndSetVolatile(this, null, hd))
                     wtail = hd;
             }
             else if (node == null)
                 node = new WNode(WMODE, p);
             else if (node.prev != p)
                 node.prev = p;
-            else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
+            else if (WTAIL.weakCompareAndSetVolatile(this, p, node)) {
                 p.next = node;
                 break;
             }
@@ -1067,11 +1079,10 @@
                     spins = HEAD_SPINS;
                 else if (spins < MAX_HEAD_SPINS)
                     spins <<= 1;
-                for (int k = spins;;) { // spin at head
+                for (int k = spins; k > 0; --k) { // spin at head
                     long s, ns;
                     if (((s = state) & ABITS) == 0L) {
-                        if (U.compareAndSwapLong(this, STATE, s,
-                                                 ns = s + WBIT)) {
+                        if (STATE.compareAndSet(this, s, ns = s + WBIT)) {
                             whead = node;
                             node.prev = null;
                             if (wasInterrupted)
@@ -1079,17 +1090,16 @@
                             return ns;
                         }
                     }
-                    else if (LockSupport.nextSecondarySeed() >= 0 &&
-                             --k <= 0)
-                        break;
+                    else
+                        Thread.onSpinWait();
                 }
             }
             else if (h != null) { // help release stale waiters
                 WNode c; Thread w;
                 while ((c = h.cowait) != null) {
-                    if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
+                    if (WCOWAIT.weakCompareAndSetVolatile(h, c, c.cowait) &&
                         (w = c.thread) != null)
-                        U.unpark(w);
+                        LockSupport.unpark(w);
                 }
             }
             if (whead == h) {
@@ -1098,7 +1108,7 @@
                         (p = np).next = node;   // stale
                 }
                 else if ((ps = p.status) == 0)
-                    U.compareAndSwapInt(p, WSTATUS, 0, WAITING);
+                    WSTATUS.compareAndSet(p, 0, WAITING);
                 else if (ps == CANCELLED) {
                     if ((pp = p.prev) != null) {
                         node.prev = pp;
@@ -1112,13 +1122,15 @@
                     else if ((time = deadline - System.nanoTime()) <= 0L)
                         return cancelWaiter(node, node, false);
                     Thread wt = Thread.currentThread();
-                    U.putObject(wt, PARKBLOCKER, this);
                     node.thread = wt;
                     if (p.status < 0 && (p != h || (state & ABITS) != 0L) &&
-                        whead == h && node.prev == p)
-                        U.park(false, time);  // emulate LockSupport.park
+                        whead == h && node.prev == p) {
+                        if (time == 0L)
+                            LockSupport.park(this);
+                        else
+                            LockSupport.parkNanos(this, time);
+                    }
                     node.thread = null;
-                    U.putObject(wt, PARKBLOCKER, null);
                     if (Thread.interrupted()) {
                         if (interruptible)
                             return cancelWaiter(node, node, true);
@@ -1146,7 +1158,7 @@
             if ((h = whead) == (p = wtail)) {
                 for (long m, s, ns;;) {
                     if ((m = (s = state) & ABITS) < RFULL ?
-                        U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
+                        STATE.compareAndSet(this, s, ns = s + RUNIT) :
                         (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) {
                         if (wasInterrupted)
                             Thread.currentThread().interrupt();
@@ -1154,8 +1166,8 @@
                     }
                     else if (m >= WBIT) {
                         if (spins > 0) {
-                            if (LockSupport.nextSecondarySeed() >= 0)
-                                --spins;
+                            --spins;
+                            Thread.onSpinWait();
                         }
                         else {
                             if (spins == 0) {
@@ -1170,7 +1182,7 @@
             }
             if (p == null) { // initialize queue
                 WNode hd = new WNode(WMODE, null);
-                if (U.compareAndSwapObject(this, WHEAD, null, hd))
+                if (WHEAD.weakCompareAndSetVolatile(this, null, hd))
                     wtail = hd;
             }
             else if (node == null)
@@ -1178,27 +1190,25 @@
             else if (h == p || p.mode != RMODE) {
                 if (node.prev != p)
                     node.prev = p;
-                else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
+                else if (WTAIL.weakCompareAndSetVolatile(this, p, node)) {
                     p.next = node;
                     break;
                 }
             }
-            else if (!U.compareAndSwapObject(p, WCOWAIT,
-                                             node.cowait = p.cowait, node))
+            else if (!WCOWAIT.compareAndSet(p, node.cowait = p.cowait, node))
                 node.cowait = null;
             else {
                 for (;;) {
                     WNode pp, c; Thread w;
                     if ((h = whead) != null && (c = h.cowait) != null &&
-                        U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
+                        WCOWAIT.compareAndSet(h, c, c.cowait) &&
                         (w = c.thread) != null) // help release
-                        U.unpark(w);
+                        LockSupport.unpark(w);
                     if (h == (pp = p.prev) || h == p || pp == null) {
                         long m, s, ns;
                         do {
                             if ((m = (s = state) & ABITS) < RFULL ?
-                                U.compareAndSwapLong(this, STATE, s,
-                                                     ns = s + RUNIT) :
+                                STATE.compareAndSet(this, s, ns = s + RUNIT) :
                                 (m < WBIT &&
                                  (ns = tryIncReaderOverflow(s)) != 0L)) {
                                 if (wasInterrupted)
@@ -1221,13 +1231,15 @@
                             return cancelWaiter(node, p, false);
                         }
                         Thread wt = Thread.currentThread();
-                        U.putObject(wt, PARKBLOCKER, this);
                         node.thread = wt;
                         if ((h != pp || (state & ABITS) == WBIT) &&
-                            whead == h && p.prev == pp)
-                            U.park(false, time);
+                            whead == h && p.prev == pp) {
+                            if (time == 0L)
+                                LockSupport.park(this);
+                            else
+                                LockSupport.parkNanos(this, time);
+                        }
                         node.thread = null;
-                        U.putObject(wt, PARKBLOCKER, null);
                         if (Thread.interrupted()) {
                             if (interruptible)
                                 return cancelWaiter(node, p, true);
@@ -1248,32 +1260,32 @@
                 for (int k = spins;;) { // spin at head
                     long m, s, ns;
                     if ((m = (s = state) & ABITS) < RFULL ?
-                        U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
+                        STATE.compareAndSet(this, s, ns = s + RUNIT) :
                         (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) {
                         WNode c; Thread w;
                         whead = node;
                         node.prev = null;
                         while ((c = node.cowait) != null) {
-                            if (U.compareAndSwapObject(node, WCOWAIT,
-                                                       c, c.cowait) &&
+                            if (WCOWAIT.compareAndSet(node, c, c.cowait) &&
                                 (w = c.thread) != null)
-                                U.unpark(w);
+                                LockSupport.unpark(w);
                         }
                         if (wasInterrupted)
                             Thread.currentThread().interrupt();
                         return ns;
                     }
-                    else if (m >= WBIT &&
-                             LockSupport.nextSecondarySeed() >= 0 && --k <= 0)
+                    else if (m >= WBIT && --k <= 0)
                         break;
+                    else
+                        Thread.onSpinWait();
                 }
             }
             else if (h != null) {
                 WNode c; Thread w;
                 while ((c = h.cowait) != null) {
-                    if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
+                    if (WCOWAIT.compareAndSet(h, c, c.cowait) &&
                         (w = c.thread) != null)
-                        U.unpark(w);
+                        LockSupport.unpark(w);
                 }
             }
             if (whead == h) {
@@ -1282,7 +1294,7 @@
                         (p = np).next = node;   // stale
                 }
                 else if ((ps = p.status) == 0)
-                    U.compareAndSwapInt(p, WSTATUS, 0, WAITING);
+                    WSTATUS.compareAndSet(p, 0, WAITING);
                 else if (ps == CANCELLED) {
                     if ((pp = p.prev) != null) {
                         node.prev = pp;
@@ -1296,14 +1308,16 @@
                     else if ((time = deadline - System.nanoTime()) <= 0L)
                         return cancelWaiter(node, node, false);
                     Thread wt = Thread.currentThread();
-                    U.putObject(wt, PARKBLOCKER, this);
                     node.thread = wt;
                     if (p.status < 0 &&
                         (p != h || (state & ABITS) == WBIT) &&
-                        whead == h && node.prev == p)
-                        U.park(false, time);
+                        whead == h && node.prev == p) {
+                            if (time == 0L)
+                                LockSupport.park(this);
+                            else
+                                LockSupport.parkNanos(this, time);
+                    }
                     node.thread = null;
-                    U.putObject(wt, PARKBLOCKER, null);
                     if (Thread.interrupted()) {
                         if (interruptible)
                             return cancelWaiter(node, node, true);
@@ -1325,7 +1339,7 @@
      * AbstractQueuedSynchronizer (see its detailed explanation in AQS
      * internal documentation).
      *
-     * @param node if nonnull, the waiter
+     * @param node if non-null, the waiter
      * @param group either node or the group node is cowaiting with
      * @param interrupted if already interrupted
      * @return INTERRUPTED if interrupted or Thread.interrupted, else zero
@@ -1337,7 +1351,7 @@
             // unsplice cancelled nodes from group
             for (WNode p = group, q; (q = p.cowait) != null;) {
                 if (q.status == CANCELLED) {
-                    U.compareAndSwapObject(p, WCOWAIT, q, q.cowait);
+                    WCOWAIT.compareAndSet(p, q, q.cowait);
                     p = group; // restart
                 }
                 else
@@ -1346,7 +1360,7 @@
             if (group == node) {
                 for (WNode r = group.cowait; r != null; r = r.cowait) {
                     if ((w = r.thread) != null)
-                        U.unpark(w);       // wake up uncancelled co-waiters
+                        LockSupport.unpark(w); // wake up uncancelled co-waiters
                 }
                 for (WNode pred = node.prev; pred != null; ) { // unsplice
                     WNode succ, pp;        // find valid successor
@@ -1357,23 +1371,23 @@
                             if (t.status != CANCELLED)
                                 q = t;     // don't link if succ cancelled
                         if (succ == q ||   // ensure accurate successor
-                            U.compareAndSwapObject(node, WNEXT,
-                                                   succ, succ = q)) {
+                            WNEXT.compareAndSet(node, succ, succ = q)) {
                             if (succ == null && node == wtail)
-                                U.compareAndSwapObject(this, WTAIL, node, pred);
+                                WTAIL.compareAndSet(this, node, pred);
                             break;
                         }
                     }
                     if (pred.next == node) // unsplice pred link
-                        U.compareAndSwapObject(pred, WNEXT, node, succ);
+                        WNEXT.compareAndSet(pred, node, succ);
                     if (succ != null && (w = succ.thread) != null) {
+                        // wake up succ to observe new pred
                         succ.thread = null;
-                        U.unpark(w);       // wake up succ to observe new pred
+                        LockSupport.unpark(w);
                     }
                     if (pred.status != CANCELLED || (pp = pred.prev) == null)
                         break;
                     node.prev = pp;        // repeat if new pred wrong/cancelled
-                    U.compareAndSwapObject(pp, WNEXT, pred, succ);
+                    WNEXT.compareAndSet(pp, pred, succ);
                     pred = pp;
                 }
             }
@@ -1397,34 +1411,22 @@
         return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L;
     }
 
-    // Unsafe mechanics
-    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
-    private static final long STATE;
-    private static final long WHEAD;
-    private static final long WTAIL;
-    private static final long WNEXT;
-    private static final long WSTATUS;
-    private static final long WCOWAIT;
-    private static final long PARKBLOCKER;
-
+    // VarHandle mechanics
+    private static final VarHandle STATE;
+    private static final VarHandle WHEAD;
+    private static final VarHandle WTAIL;
+    private static final VarHandle WNEXT;
+    private static final VarHandle WSTATUS;
+    private static final VarHandle WCOWAIT;
     static {
         try {
-            STATE = U.objectFieldOffset
-                (StampedLock.class.getDeclaredField("state"));
-            WHEAD = U.objectFieldOffset
-                (StampedLock.class.getDeclaredField("whead"));
-            WTAIL = U.objectFieldOffset
-                (StampedLock.class.getDeclaredField("wtail"));
-
-            WSTATUS = U.objectFieldOffset
-                (WNode.class.getDeclaredField("status"));
-            WNEXT = U.objectFieldOffset
-                (WNode.class.getDeclaredField("next"));
-            WCOWAIT = U.objectFieldOffset
-                (WNode.class.getDeclaredField("cowait"));
-
-            PARKBLOCKER = U.objectFieldOffset
-                (Thread.class.getDeclaredField("parkBlocker"));
+            MethodHandles.Lookup l = MethodHandles.lookup();
+            STATE = l.findVarHandle(StampedLock.class, "state", long.class);
+            WHEAD = l.findVarHandle(StampedLock.class, "whead", WNode.class);
+            WTAIL = l.findVarHandle(StampedLock.class, "wtail", WNode.class);
+            WSTATUS = l.findVarHandle(WNode.class, "status", int.class);
+            WNEXT = l.findVarHandle(WNode.class, "next", WNode.class);
+            WCOWAIT = l.findVarHandle(WNode.class, "cowait", WNode.class);
         } catch (ReflectiveOperationException e) {
             throw new Error(e);
         }
--- a/jdk/test/java/util/concurrent/tck/StampedLockTest.java	Wed Jul 05 21:58:29 2017 +0200
+++ b/jdk/test/java/util/concurrent/tck/StampedLockTest.java	Fri Jul 15 13:51:43 2016 -0700
@@ -745,28 +745,41 @@
     public void testTryConvertToOptimisticRead() throws InterruptedException {
         StampedLock lock = new StampedLock();
         long s, p;
-        s = 0L;
-        assertFalse((p = lock.tryConvertToOptimisticRead(s)) != 0L);
+        assertEquals(0L, lock.tryConvertToOptimisticRead(0L));
+
         assertTrue((s = lock.tryOptimisticRead()) != 0L);
-        assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
+        assertEquals(s, lock.tryConvertToOptimisticRead(s));
+        assertTrue(lock.validate(s));
+
+        assertTrue((p = lock.readLock()) != 0L);
+        assertTrue((s = lock.tryOptimisticRead()) != 0L);
+        assertEquals(s, lock.tryConvertToOptimisticRead(s));
+        assertTrue(lock.validate(s));
+        lock.unlockRead(p);
+
         assertTrue((s = lock.writeLock()) != 0L);
         assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
         assertTrue(lock.validate(p));
+
         assertTrue((s = lock.readLock()) != 0L);
         assertTrue(lock.validate(s));
         assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
         assertTrue(lock.validate(p));
+
         assertTrue((s = lock.tryWriteLock()) != 0L);
         assertTrue(lock.validate(s));
         assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
         assertTrue(lock.validate(p));
+
         assertTrue((s = lock.tryReadLock()) != 0L);
         assertTrue(lock.validate(s));
         assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
         assertTrue(lock.validate(p));
+
         assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
         assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
         assertTrue(lock.validate(p));
+
         assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
         assertTrue(lock.validate(s));
         assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
@@ -780,39 +793,67 @@
     public void testTryConvertToReadLock() throws InterruptedException {
         StampedLock lock = new StampedLock();
         long s, p;
-        s = 0L;
-        assertFalse((p = lock.tryConvertToReadLock(s)) != 0L);
+
+        assertFalse((p = lock.tryConvertToReadLock(0L)) != 0L);
+
         assertTrue((s = lock.tryOptimisticRead()) != 0L);
         assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
+        assertTrue(lock.isReadLocked());
+        assertEquals(1, lock.getReadLockCount());
         lock.unlockRead(p);
+
+        assertTrue((s = lock.tryOptimisticRead()) != 0L);
+        lock.readLock();
+        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
+        assertTrue(lock.isReadLocked());
+        assertEquals(2, lock.getReadLockCount());
+        lock.unlockRead(p);
+        lock.unlockRead(p);
+
         assertTrue((s = lock.writeLock()) != 0L);
         assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
         assertTrue(lock.validate(p));
+        assertTrue(lock.isReadLocked());
+        assertEquals(1, lock.getReadLockCount());
         lock.unlockRead(p);
+
         assertTrue((s = lock.readLock()) != 0L);
         assertTrue(lock.validate(s));
-        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
-        assertTrue(lock.validate(p));
-        lock.unlockRead(p);
+        assertEquals(s, lock.tryConvertToReadLock(s));
+        assertTrue(lock.validate(s));
+        assertTrue(lock.isReadLocked());
+        assertEquals(1, lock.getReadLockCount());
+        lock.unlockRead(s);
+
         assertTrue((s = lock.tryWriteLock()) != 0L);
         assertTrue(lock.validate(s));
         assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
         assertTrue(lock.validate(p));
+        assertEquals(1, lock.getReadLockCount());
         lock.unlockRead(p);
+
         assertTrue((s = lock.tryReadLock()) != 0L);
         assertTrue(lock.validate(s));
-        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
-        assertTrue(lock.validate(p));
-        lock.unlockRead(p);
+        assertEquals(s, lock.tryConvertToReadLock(s));
+        assertTrue(lock.validate(s));
+        assertTrue(lock.isReadLocked());
+        assertEquals(1, lock.getReadLockCount());
+        lock.unlockRead(s);
+
         assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
         assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
         assertTrue(lock.validate(p));
+        assertTrue(lock.isReadLocked());
+        assertEquals(1, lock.getReadLockCount());
         lock.unlockRead(p);
+
         assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
         assertTrue(lock.validate(s));
-        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
-        assertTrue(lock.validate(p));
-        lock.unlockRead(p);
+        assertEquals(s, lock.tryConvertToReadLock(s));
+        assertTrue(lock.validate(s));
+        assertTrue(lock.isReadLocked());
+        assertEquals(1, lock.getReadLockCount());
+        lock.unlockRead(s);
     }
 
     /**
@@ -822,38 +863,52 @@
     public void testTryConvertToWriteLock() throws InterruptedException {
         StampedLock lock = new StampedLock();
         long s, p;
-        s = 0L;
-        assertFalse((p = lock.tryConvertToWriteLock(s)) != 0L);
+
+        assertFalse((p = lock.tryConvertToWriteLock(0L)) != 0L);
+
         assertTrue((s = lock.tryOptimisticRead()) != 0L);
         assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
+        assertTrue(lock.isWriteLocked());
         lock.unlockWrite(p);
+
         assertTrue((s = lock.writeLock()) != 0L);
-        assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
-        assertTrue(lock.validate(p));
-        lock.unlockWrite(p);
+        assertEquals(s, lock.tryConvertToWriteLock(s));
+        assertTrue(lock.validate(s));
+        assertTrue(lock.isWriteLocked());
+        lock.unlockWrite(s);
+
         assertTrue((s = lock.readLock()) != 0L);
         assertTrue(lock.validate(s));
         assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
         assertTrue(lock.validate(p));
+        assertTrue(lock.isWriteLocked());
         lock.unlockWrite(p);
+
         assertTrue((s = lock.tryWriteLock()) != 0L);
         assertTrue(lock.validate(s));
-        assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
-        assertTrue(lock.validate(p));
-        lock.unlockWrite(p);
+        assertEquals(s, lock.tryConvertToWriteLock(s));
+        assertTrue(lock.validate(s));
+        assertTrue(lock.isWriteLocked());
+        lock.unlockWrite(s);
+
         assertTrue((s = lock.tryReadLock()) != 0L);
         assertTrue(lock.validate(s));
         assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
         assertTrue(lock.validate(p));
+        assertTrue(lock.isWriteLocked());
         lock.unlockWrite(p);
+
         assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
         assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
         assertTrue(lock.validate(p));
+        assertTrue(lock.isWriteLocked());
         lock.unlockWrite(p);
+
         assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
         assertTrue(lock.validate(s));
         assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
         assertTrue(lock.validate(p));
+        assertTrue(lock.isWriteLocked());
         lock.unlockWrite(p);
     }
 
@@ -903,4 +958,124 @@
         assertTrue(lock.tryLock());
     }
 
+    /**
+     * Lock.newCondition throws UnsupportedOperationException
+     */
+    public void testLockViewsDoNotSupportConditions() {
+        StampedLock sl = new StampedLock();
+        assertThrows(UnsupportedOperationException.class,
+                     () -> sl.asWriteLock().newCondition(),
+                     () -> sl.asReadLock().newCondition(),
+                     () -> sl.asReadWriteLock().writeLock().newCondition(),
+                     () -> sl.asReadWriteLock().readLock().newCondition());
+    }
+
+    /**
+     * Passing optimistic read stamps to unlock operations result in
+     * IllegalMonitorStateException
+     */
+    public void testCannotUnlockOptimisticReadStamps() {
+        Runnable[] actions = {
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryOptimisticRead();
+                assertTrue(stamp != 0);
+                sl.unlockRead(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryOptimisticRead();
+                sl.unlock(stamp);
+            },
+
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryOptimisticRead();
+                sl.writeLock();
+                sl.unlock(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryOptimisticRead();
+                sl.readLock();
+                sl.unlockRead(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryOptimisticRead();
+                sl.readLock();
+                sl.unlock(stamp);
+            },
+
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryConvertToOptimisticRead(sl.writeLock());
+                assertTrue(stamp != 0);
+                sl.writeLock();
+                sl.unlockWrite(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryConvertToOptimisticRead(sl.writeLock());
+                sl.writeLock();
+                sl.unlock(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryConvertToOptimisticRead(sl.writeLock());
+                sl.readLock();
+                sl.unlockRead(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryConvertToOptimisticRead(sl.writeLock());
+                sl.readLock();
+                sl.unlock(stamp);
+            },
+
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryConvertToOptimisticRead(sl.readLock());
+                assertTrue(stamp != 0);
+                sl.writeLock();
+                sl.unlockWrite(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryConvertToOptimisticRead(sl.readLock());
+                sl.writeLock();
+                sl.unlock(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryConvertToOptimisticRead(sl.readLock());
+                sl.readLock();
+                sl.unlockRead(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                sl.readLock();
+                long stamp = sl.tryConvertToOptimisticRead(sl.readLock());
+                assertTrue(stamp != 0);
+                sl.readLock();
+                sl.unlockRead(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                long stamp = sl.tryConvertToOptimisticRead(sl.readLock());
+                sl.readLock();
+                sl.unlock(stamp);
+            },
+            () -> {
+                StampedLock sl = new StampedLock();
+                sl.readLock();
+                long stamp = sl.tryConvertToOptimisticRead(sl.readLock());
+                sl.readLock();
+                sl.unlock(stamp);
+            },
+        };
+
+        assertThrows(IllegalMonitorStateException.class, actions);
+    }
+
 }