java Lock Improvement Read-Write Lock StampedLock Details

Keywords: less JDK

1. StampedLock characteristics

StampedLock is a new read-write lock for JDK 8. Unlike read-write locks, it is not implemented by AQS.Its state is a long variable, state is designed differently from read-write locks, provides three modes to control read/write acquisition, and implements its own synchronous wait queue internally.

1.1, StampedLock read-write lock

Write lock: Use the writeLock method to acquire a lock that will block when it is not available, and return a stamp corresponding to the write lock after successful acquisition. In the unlockWrite method, this stamp is required to release the corresponding lock.This stamp is also available in tryWriteLock.When a write lock is acquired in write mode, the read lock cannot be acquired, and all optimistic read lock validation (validate method) fails.
Read Lock: Use the readLock method to obtain a lock that will block when more resources are available (similar to the AQS state design).Similarly, stamp is returned after successful acquisition of the lock for the same purpose as described above.The same is true for tryReadLock.
Optimistic read lock: Obtained using the tryOptimisticRead method, an optimistic read lock can be successfully acquired only when a write lock is available, and a stamp will be returned after successful acquisition.The validate method can use this stamp to determine if a write lock has been acquired.This pattern can be understood as a weak version of a read-lock, which can be broken at any time.Optimistic read mode is often used in short read-only code segments to reduce contention and improve throughput.Optimistic reading areas should read only fields and save them in local variables for use after validation (the validate method).Field reads may be inconsistent in optimistic reading mode, so validate() may need to be called repeatedly to check consistency.For example, these steps are usually necessary when you first read an object or array reference and then access one of its fields, elements, or methods.

1.2, Transition of Three Modes

StampedLock can conditionally convert locks to each other.
Convert other locks to write locks tryConvertToWriteLock():
The current postmark is in write-lock mode and returns the current postmark directly.
If the current postmark is in read lock mode, the read lock is released, the write lock is acquired, and the write lock postmark is returned.
Current postmark holds optimistic lock, write lock is acquired immediately through CAS, write lock postmark is returned for success, 0 for failure;

Convert other locks to read locks tryConvertToReadLock:
If the current postmark is in write lock mode, the write lock is released, the read lock is acquired, and the read lock postmark is returned.
If the current postmark is in read lock mode, the current read lock postmark is returned directly.
The current postmark holds an optimistic lock, and if the read lock is acquired immediately through CAS, the read lock postmark is returned; otherwise, the acquisition failure returns 0;

Convert other locks to optimistic locks tryConvertToOptimisticRead:
If the current postmark holds a read or write lock, the read/write lock is released directly and the observed postmark value is returned.
The current postmark holds an optimistic lock. If the optimistic lock postmark is valid, the observer postmark is returned.

1.3. Application scenarios for StampedLock

StampedLock is generally an internal tool class for thread security.Its use depends on a certain understanding of the internal properties of data, objects, and methods.StampedLock is non-reentrant, so other parties trying to acquire a lock repeatedly cannot be called inside the lock.If a stamp has not been used or validated for a long time, validation may fail after a long time.StampedLocks is serializable, but it becomes initially unlocked after deserialization, so it is not safe in remote locking.

1.4. Equity of StampedLock

StampedLock's scheduling strategy does not always favor read or write threads. All "try" methods are acquired to the best of their ability and do not necessarily follow any scheduling or fair policies.When a lock acquired or converted from the'try'method fails to return 0, no lock status information is carried.Because StampedLock supports coordinated use across multiple lock modes, it does not directly implement the Lock or ReadWriteLock interfaces.However, if the application requires Lock-related functionality, it can return a Lock view through the asReadLock(), asWriteLock(), and asReadWriteLock() methods.

2. Source Code Analysis

2.1. Main attributes

//Gets the number of threads available to the CPU to determine the number of loops when spinning
private static final int NCPU = Runtime.getRuntime().availableProcessors();

//Determine the limit on the number of spins based on NCPU (not necessarily this many times, because the actual code is random)
private static final int SPINS = (NCPU > 1) ? 1 << 6 : 0;

//Number of spins on the head node
private static final int HEAD_SPINS = (NCPU > 1) ? 1 << 10 : 0;

//Maximum number of spins on the head node
private static final int MAX_HEAD_SPINS = (NCPU > 1) ? 1 << 16 : 0;

//Number of cycles waiting for spin lock overflow
private static final int OVERFLOW_YIELD_RATE = 7; // must be power 2 - 1

//Number of bit s used to read thread count before overflow
private static final int LG_READERS = 7;

//One Read Status Unit: 0000 0000 0001
private static final long RUNIT = 1L;
//A write status unit: 0000 1000 0000
private static final long WBIT  = 1L << LG_READERS;
//Read Status Identification: 0000 0111 1111
private static final long RBITS = WBIT - 1L;
//Maximum read lock count: 0000 0111 1110
private static final long RFULL = RBITS - 1L;
//Mask for number of read and write threads: 0000 1111 1111
private static final long ABITS = RBITS | WBIT;
////Read the inverse of the number of threads, all 25 bits high are 1:1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 10000000
private static final long SBITS = ~RBITS; // note overlap with ABITS

// Initial value of state
private static final long ORIGIN = WBIT << 1;

// Interrupt Identification
private static final long INTERRUPTED = 1L;

// Node status value, wait/cancel
private static final int WAITING   = -1;
private static final int CANCELLED =  1;

// Node mode, read/write mode
private static final int RMODE = 0;
private static final int WMODE = 1;


//Head node of waiting queue
private transient volatile WNode whead;
//Waiting for the end node of the queue
private transient volatile WNode wtail;

// Lock state
private transient volatile long state;
////Because the read state is only 7-bit small, this variable will be used to record when it exceeds 128
private transient int readerOverflow;

2.2, node implementation

//Node implementation waiting for queue
static final class WNode {
    //Precursor Node
    volatile WNode prev;
    //Successor Node
    volatile WNode next;
    //Link list used by read threads
    volatile WNode cowait;    // list of linked readers
    //Waiting Threads
    volatile Thread thread;   // non-null while possibly parked
    //Node State
    volatile int status;      // 0, WAITING, or CANCELLED
    //Node mode
    final int mode;           // RMODE or WMODE
    WNode(int m, WNode p) { mode = m; prev = p; }
}

2.3. State state implementation

state status description:

  • Bit 0 - bit6 is the read lock count, and readerOverflow is the read lock count when RFULL (126) is exceeded.State adds RUINT when a read lock is acquired (value 1); state subtracts RUINT when a read lock is released.
  • bit7 is a write lock identifier, with a value of 1 indicating that a write lock has been acquired and a value of 0 indicating that a read lock has been acquired.When a thread acquires or releases a write lock, it adds a state to WBIT;
  • bit8 - bit64: Represents a write lock version whose value changes whether a write lock is acquired or released.
    Initial state bit8 is 1, other bits are 0.

state Common Status Identification:

  • RUNIT:
    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001
  • WBIT:
    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1000 0000
  • RBITS:
    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0111 1111
  • RFULL:
    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0111 1110
  • ABITS:
    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1111 1111
  • SBITS:
    1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1000 0000
  • ORIGIN:
    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000

State common state judgment:

  • There is a wireless process to get write status: state < WBIT, true: none, false: yes;
  • Read status overflow: (state & ABITS) < RFULL, true; no; false: yes;
  • Get read status: state + RUNIT (or readerOverflow + 1)
  • Get write status: state + WBIT
  • Release read state: state - RUNIT (or readerOverflow - 1)
  • Release Write State: (s += WBIT) == 0L? ORIGIN: s
  • Is it a write lock: (state & WBIT)!= 0L
  • Is it a read lock: (state & RBITS)!= 0L

2.4. Write lock acquisition and release

Acquire write lock:

public long writeLock() {
    long s, next;  
    //(state & ABITS) Get the lower 8 bits to determine if a read/write lock exists.
    //For other read locks, bit0-bit6 is not zero, and for other write locks, bit7 is not zero.
    //So if there are other write locks or read locks, it will fail
    //Attempting CAS to acquire a write lock
    //Failed call acquireWrite to continue acquiring write locks
    return ((((s = state) & ABITS) == 0L &&
             U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
            next : acquireWrite(false, 0L));
}
private long acquireWrite(boolean interruptible, long deadline) {
    //Node is the current node, p is the precursor of the current node
    WNode node = null, p;
    
    // First spin - mainly for team entry
    for (int spins = -1;;) { // spin while enqueuing
        long m, s, ns;
        //(state&ABITS) 0 means no read/write lock, try CAS to acquire write lock
        if ((m = (s = state) & ABITS) == 0L) {
            if (U.compareAndSwapLong(this, STATE, s, ns = s + WBIT))
                return ns;
        }
        else if (spins < 0)
            // If the number of spins is less than 0, the number of spins is calculated
            // If a write lock (m == WBIT) is currently exclusive and the queue has no elements (wtail == whead),
            // Indicates that the precursor node of the current node is the node that acquires an exclusive lock and the lock will be released soon.
            // Just spin SPINS times, if it's not your turn to join the team
            // Otherwise, the number of spins is zero
            spins = (m == WBIT && wtail == whead) ? SPINS : 0;
        else if (spins > 0) {
            // When the number of spins is greater than 0, the current spin is randomly reduced by one spin
            if (LockSupport.nextSecondarySeed() >= 0)
                --spins;
        }
        else if ((p = wtail) == null) { // initialize queue
            // If the queue is not initialized, create a new empty node and initialize the header and tail nodes
            WNode hd = new WNode(WMODE, null);
            if (U.compareAndSwapObject(this, WHEAD, null, hd))
                wtail = hd;
        }
        else if (node == null)
            // If the new node has not been initialized, create it and assign its preceding node to the tail node
            node = new WNode(WMODE, p);
        else if (node.prev != p)
            // If the precursor node of the new node is not the tail node,
            // The precursor node that updates the new node is the new tail node
            node.prev = p;
        else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
            // If the attempt to update the new node to a new tail node succeeds, the loop exits
            p.next = node;
            break;
        }
    }

    // Second spin - mainly blocking and waiting to wake up
    for (int spins = -1;;) {
        // h is the head node, np is the front node of the new node, pp is the front node, ps is the state of the front node
        WNode h, np, pp; int ps;
        // If the head node equals the leading node, it's almost your turn
        if ((h = whead) == p) {
            if (spins < 0)
                // If the number of spins is less than 0, the number of initial spins is
                spins = HEAD_SPINS;
            else if (spins < MAX_HEAD_SPINS)
                // If the number of spins is less than the maximum number of spins of the head node, the number of spins is increased
                spins <<= 1;
                
            // Third spin, keep trying to acquire write locks    
            for (int k = spins;;) { // spin at head
                long s, ns;
                //CAS acquires write locks without read-write locks and updates node information with success
                if (((s = state) & ABITS) == 0L) {
                    if (U.compareAndSwapLong(this, STATE, s,
                                             ns = s + WBIT)) {
                        whead = node;
                        node.prev = null;
                        return ns;
                    }
                }
                // Random spin number, jump out of the loop and try again when the spin number is reduced to 0
                else if (LockSupport.nextSecondarySeed() >= 0 &&
                         --k <= 0)
                    break;
            }
        }
        //Is the header node empty?
        else if (h != null) { // help release stale waiters
            WNode c; Thread w;
            // If the cowait list (stack) of the header node is not empty, wake up all the nodes inside
            while ((c = h.cowait) != null) {
                if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
                    (w = c.thread) != null)
                    U.unpark(w);
            }
        }
        
        // If the header node does not change
        if (whead == h) {
            // Update if tail node changes
            if ((np = node.prev) != p) {
                if (np != null)
                    (p = np).next = node;   // stale
            }
            else if ((ps = p.status) == 0)
                // Update to WAITING if tail node status is 0
                U.compareAndSwapInt(p, WSTATUS, 0, WAITING);
            else if (ps == CANCELLED) {
                // If the tail node is canceled, remove it from the list
                if ((pp = p.prev) != null) {
                    node.prev = pp;
                    pp.next = node;
                }
            }
            else {
                // Processing with timeout
                long time; // 0 argument to park means no timeout
                if (deadline == 0L)
                    time = 0L;
                //Cancel node when time expires    
                else if ((time = deadline - System.nanoTime()) <= 0L)
                    return cancelWaiter(node, node, false);
                    
                //Set Thread blocker    
                Thread wt = Thread.currentThread();
                U.putObject(wt, PARKBLOCKER, this);
                node.thread = wt;
                //1. The current precursor node state is WAITING;
                //2. The precursor node of the current node is not a header node or has a read-write lock already acquired;
                //3. Head node changes;
                //4. The precursor node of the current node has not changed;
                //Block the current node when all four conditions are met
                if (p.status < 0 && (p != h || (state & ABITS) != 0L) &&
                    whead == h && node.prev == p)
                    U.park(false, time);  // emulate LockSupport.park
                node.thread = null;
                //Set thread blocker to null
                U.putObject(wt, PARKBLOCKER, null);
                //Cancel node if current node 100 interrupts
                if (interruptible && Thread.interrupted())
                    return cancelWaiter(node, node, true);
            }
        }
    }
}

Write lock release:

public void unlockWrite(long stamp) {
    WNode h;
    //Because write locks are exclusive locks, it is easy to judge the state!= stamp;
    //Or bit7 is 0, that is, stamp state is write-free
    if (state != stamp || (stamp & WBIT) == 0L)
        throw new IllegalMonitorStateException();
    
    //Modify the state state, state += WBIT; overflow initializes to ORIGIN        
    state = (stamp += WBIT) == 0L ? ORIGIN : stamp;
    //The head node is not empty and in normal state releases the lock on the head node
    if ((h = whead) != null && h.status != 0)
        release(h);
}
private void release(WNode h) {
    if (h != null) {
        WNode q; Thread w;
        //Set header node status to 0
        U.compareAndSwapInt(h, WSTATUS, WAITING, 0);
        
        //If the next node of the head node is empty or the state is CANCEL, it traverses forward from the tail node.
        //Find the first valid node after the header node (status 0 or WAITTING)
        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;
        }
        //Wake up the next valid node
        if (q != null && (w = q.thread) != null)
            U.unpark(w);
    }
}

2.5. Read lock acquisition and release

Read lock acquisition:

public long readLock() {
    long s = state, next;  // bypass acquireRead on common uncontended case
    //The synchronization queue is empty, the read lock count value does not exceed the maximum value (no write lock). If CAS acquires a lock successfully, the postmark value is returned directly.
    //Otherwise acquireRead acquires the read lock
    return ((whead == wtail && (s & ABITS) < RFULL &&
             U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ?
            next : acquireRead(false, 0L));
}
private long acquireRead(boolean interruptible, long deadline) {
    WNode node = null, p;
    for (int spins = -1;;) {
        WNode h;
        //If the synchronization queue head node equals the tail node, there is no node or only one node in the queue
        //Then spin for a while and wait until the head node releases the lock to acquire it
        if ((h = whead) == (p = wtail)) {
            for (long m, s, ns;;) {
                //(state & ABITS) is less than RFULL, meaning no write lock, CAS acquires read lock directly;
                //Otherwise, if (state & ABITS) is less than WBIT, there is no write lock but the read lock count has overflowed.
                //CAS updates the read lock count to acquire a read lock
                if ((m = (s = state) & ABITS) < RFULL ?
                    U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
                    (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L))
                    return ns;
                //(state & ABITS) is greater than WBIT, indicating that the write lock has been occupied, then spin    
                else if (m >= WBIT) {
                    //Random spin reduction
                    if (spins > 0) {
                        if (LockSupport.nextSecondarySeed() >= 0)
                            --spins;
                    }
                    else {
                        //Have you not acquired a read lock yet after spinning?
                        if (spins == 0) {
                            WNode nh = whead, np = wtail;
                            //Judge stability (whether modified), jump out of cycle
                            if ((nh == h && np == p) || (h = nh) != (p = np))
                                break;
                        }
                        //Initialize spins
                        spins = SPINS;
                    }
                }
            }
        }
        //If spin acquisition fails, node initialization-related processing occurs
        //Initialize queue if tail node is empty
        if (p == null) { // initialize queue
            WNode hd = new WNode(WMODE, null);
            if (U.compareAndSwapObject(this, WHEAD, null, hd))
                wtail = hd;
        }
        //Initialize the node representing the current read thread
        else if (node == null)
            node = new WNode(RMODE, p);
        //head==tail or queue tail.mode is not read,
        //Then add the node node of the current thread to the end of the queue and jump out of the outer loop    
        else if (h == p || p.mode != RMODE) {
            if (node.prev != p)
                node.prev = p;
            else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
                p.next = node;
                break;
            }
        }
        //If head!= tail indicates that a thread is already waiting in the queue or tail.mode l is a read state RMODE,
        //Then CAS adds the current thread's node node node to the tail node's cowait chain
        else if (!U.compareAndSwapObject(p, WCOWAIT,
                                         node.cowait = p.cowait, node))
            node.cowait = null;
        else {
            for (;;) {
                WNode pp, c; Thread w;
                ////If the header is not empty then try to free the nodes in the cowait chain of the header
                if ((h = whead) != null && (c = h.cowait) != null &&
                    U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
                    (w = c.thread) != null) // help release
                    U.unpark(w);
                //If the precursor of the tail node is head or head==tail or the precursor of the tail node is null
                //That is, the node where the current node is located (because the node may be in the cowait chain)
                //The predecessor is the head or the head has been released as null    
                if (h == (pp = p.prev) || h == p || pp == null) {
                    long m, s, ns;
                    do {
                        //If no write state is occupied then spin attempts to get the read state and stamp is returned successfully
                        if ((m = (s = state) & ABITS) < RFULL ?
                            U.compareAndSwapLong(this, STATE, s,
                                                 ns = s + RUNIT) :
                            (m < WBIT &&
                             (ns = tryIncReaderOverflow(s)) != 0L))
                            return ns;
                    } while (m < WBIT);
                }
                //Judging stability
                if (whead == h && p.prev == pp) {
                    long time;
                    //If tail's predecessor is null or head==tail or tail has been cancelled (p.status > 0)
                    //Set the node directly as null to jump out of the loop and go back to the open for loop to try to get the synchronization status again
                    if (pp == null || h == p || p.status > 0) {
                        node = null; // throw away
                        break;
                    }
                    if (deadline == 0L)
                        time = 0L;
                    //Cancel current thread if timeout occurs    
                    else if ((time = deadline - System.nanoTime()) <= 0L)
                        return cancelWaiter(node, p, false);
                    Thread wt = Thread.currentThread();
                    U.putObject(wt, PARKBLOCKER, this);
                    node.thread = wt;
                    
                    //tail predecessor is not head er or only write threads are currently in sync state
                    //Judging stability
                    if ((h != pp || (state & ABITS) == WBIT) &&
                        whead == h && p.prev == pp)
                        U.park(false, time);
                    node.thread = null;
                    U.putObject(wt, PARKBLOCKER, null);
                    //Cancel if interrupted
                    if (interruptible && Thread.interrupted())
                        return cancelWaiter(node, p, true);
                }
            }
        }
    }

     //If there are no nodes in the queue or tail's mode l is WMODE write state,
     //Then the node enters the loop after it is added to the tail queue
    for (int spins = -1;;) {
        WNode h, np, pp; int ps;
        //If P (the precursor node of a node) is the head, the spin mode attempts to get the synchronization state
        if ((h = whead) == p) {
            //First cycle, set the number of spins
            if (spins < 0)
                spins = HEAD_SPINS;
            //Spin number increase    
            else if (spins < MAX_HEAD_SPINS)
                spins <<= 1;
            for (int k = spins;;) { // spin at head
                long m, s, ns;
                //Spin Attempt to Get Synchronization Status
                //If successful, set the node as head and free the node in the node's cowait chain and return stamp
                if ((m = (s = state) & ABITS) < RFULL ?
                    U.compareAndSwapLong(this, STATE, 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) &&
                            (w = c.thread) != null)
                            U.unpark(w);
                    }
                    return ns;
                }
                //Random--k controls the number of loops if a write thread gets synchronized (because a write thread may break in)
                else if (m >= WBIT &&
                         LockSupport.nextSecondarySeed() >= 0 && --k <= 0)
                    break;
            }
        }
        //If the head is not null, free the nodes in the cowait chain of the head
        else if (h != null) {
            WNode c; Thread w;
            while ((c = h.cowait) != null) {
                if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
                    (w = c.thread) != null)
                    U.unpark(w);
            }
        }
         //Judging stability
        if (whead == h) {
            if ((np = node.prev) != p) {
                if (np != null)
                    (p = np).next = node;   // stale
            }
            //Attempt to set tail's state bit WAITING to indicate that there are waiting nodes behind
            else if ((ps = p.status) == 0)
                U.compareAndSwapInt(p, WSTATUS, 0, WAITING);
            //If tail has been canceled    
            else if (ps == CANCELLED) {
                if ((pp = p.prev) != null) {
                    node.prev = pp;
                    pp.next = node;
                }
            }
            else {
                //Timeout determination
                long time;
                if (deadline == 0L)
                    time = 0L;
                else if ((time = deadline - System.nanoTime()) <= 0L)
                    return cancelWaiter(node, node, false);
                Thread wt = Thread.currentThread();
                U.putObject(wt, PARKBLOCKER, this);
                node.thread = wt;
                //Blocking Wait
                if (p.status < 0 &&
                    (p != h || (state & ABITS) == WBIT) &&
                    whead == h && node.prev == p)
                    U.park(false, time);
                node.thread = null;
                U.putObject(wt, PARKBLOCKER, null);
                //Interrupt handling
                if (interruptible && Thread.interrupted())
                    return cancelWaiter(node, node, true);
            }
        }
    }
}

Read lock release:

public void unlockRead(long stamp) {
    long s, m; WNode h;
    for (;;) {
        //If the bit bit bit associated with the write count changes, or the bit associated with the read count is 0, or if there is a write lock, then the exception is
        if (((s = state) & SBITS) != (stamp & SBITS) ||
            (stamp & ABITS) == 0L || (m = s & ABITS) == 0L || m == WBIT)
            throw new IllegalMonitorStateException();
        //Read lock count not overflowing?    
        if (m < RFULL) {
            if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
                if (m == RUNIT && (h = whead) != null && h.status != 0)
                    release(h);
                break;
            }
        }
       //Read lock count overflow?    
        else if (tryDecReaderOverflow(s) != 0L)
            break;
    }
}

2.6, Optimistic Lock Acquisition

public long tryOptimisticRead() {
    long s;
    //When there is no write lock, it returns (state & SBITS), which is the high bit write count;
    //Otherwise, return 0, fail to get optimistic lock
    return (((s = state) & WBIT) == 0L) ? (s & SBITS) : 0L;
}

2.7. Conversion of lock mode

Convert to Write Lock: Convert locks in other modes such as Write, Read, Optimistic to Write

public long tryConvertToWriteLock(long stamp) {
    long a = stamp & ABITS, m, s, next;
    //Write lock status unchanged?
    while (((s = state) & SBITS) == (stamp & SBITS)) {
        //Currently no write/read lock?CAS Acquire Write Lock
        if ((m = s & ABITS) == 0L) {
            if (a != 0L)
                break;
            if (U.compareAndSwapLong(this, STATE, s, next = s + WBIT))
                return next;
        }
        //If there is a write lock and the state is unchanged, the original version postmark is returned directly
        else if (m == WBIT) {
            //Write thread is not the current thread?Error, jump out of the loop
            if (a != m)
                break;
            return stamp;
        }
        //No write lock, there is a read lock, that is, the current thread is the thread that acquires the read lock
        //Direct cas releases the read lock and acquires the write lock, state= (state - RUNIT + WBIT)
        else if (m == RUNIT && a != 0L) {
            if (U.compareAndSwapLong(this, STATE, s,
                                     next = s - RUNIT + WBIT))
                return next;
        }
        //Spin waits for other read locks to release when other threads acquire them
        else
            break;
    }
    return 0L;
}

Main Processing:
Write locks are acquired through CAS when the lock state is unread/write;
Returns the original postmark directly when the current thread has acquired a write lock;
When there is only one read lock, that is, when only the current thread acquires a read lock and no other read locks exist, CAS releases the read lock and acquires the write lock.
When the number of threads acquiring read locks is greater than 1, that is, when other threads besides the current thread acquire read locks, spin waits for other read locks to be released.

Convert to read lock: Convert read/write lock to read lock;

public long tryConvertToReadLock(long stamp) {
    long a = stamp & ABITS, m, s, next; WNode h;
    //No write lock?
    while (((s = state) & SBITS) == (stamp & SBITS)) {
        
        //No read/write lock, CAS acquires read lock
        if ((m = s & ABITS) == 0L) {
            //Wrong postmark?Exit Loop
            if (a != 0L)
                break;
            //Read lock count not overflowing?    
            else if (m < RFULL) {
                if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
                    return next;
            }
            //Read lock count overflow?
            else if ((next = tryIncReaderOverflow(s)) != 0L)
                return next;
        }
        //Is there a write lock and the write lock is the current thread?Release the read lock and wake up subsequent write lock threads
        else if (m == WBIT) {
            //Write lock is not current thread, error, jump out of cycle
            if (a != m)
                break;
            state = next = s + (WBIT + RUNIT);
            if ((h = whead) != null && h.status != 0)
                release(h);
            return next;
        }
        //Has the current thread acquired a read lock?
        else if (a != 0L && a < WBIT)
            return stamp;
        else
            break;
    }
    return 0L;
}

Main Processing:
CAS acquires a read lock if there is no read/write lock;
If the write lock is the current thread, CAS releases the write lock and acquires the read lock.
Returns the original postmark if the current thread has acquired a read lock;

Convert to Optimistic Lock:

public long tryConvertToOptimisticRead(long stamp) {
    long a = stamp & ABITS, m, s, next; WNode h;
    U.loadFence();
    for (;;) {
        //Write Lock State Change?
        if (((s = state) & SBITS) != (stamp & SBITS))
            break;
        //No read/write lock?Return directly to state    
        if ((m = s & ABITS) == 0L) {
            if (a != 0L)
                break;
            return s;
        }
        //Write lock acquired?Release the write lock directly
        else if (m == WBIT) {
            if (a != m)
                break;
            state = next = (s += WBIT) == 0L ? ORIGIN : s;
            if ((h = whead) != null && h.status != 0)
                release(h);
            return next;
        }
        //Is the state unread or write-free?
        else if (a == 0L || a >= WBIT)
            break;
        //Read Lock Count Not Overflowed    
        else if (m < RFULL) {
            if (U.compareAndSwapLong(this, STATE, s, next = s - RUNIT)) {
                if (m == RUNIT && (h = whead) != null && h.status != 0)
                    release(h);
                return next & SBITS;
            }
        }
        //Read lock count overflow?
        else if ((next = tryDecReaderOverflow(s)) != 0L)
            return next & SBITS;
    }
    return 0L;
}

Main Processing:
If the current thread has acquired a write lock, the write lock is released directly.
If the current thread has acquired a read lock, CAS releases the read lock.
Returns the state value when locked;

Posted by Xephon on Thu, 18 Jul 2019 17:25:06 -0700