AQS ReentrantReadWriteLock write lock

1. Usage

1.1 Define a safe list collection

public class LockDemo  {
    
    
ArrayList<Integer> arrayList = new ArrayList<>();//定义一个集合
// 定义读锁
ReentrantReadWriteLock.ReadLock readLock = new    ReentrantReadWriteLock(true).readLock();
// 定义写锁
ReentrantReadWriteLock.WriteLock writeLock = new ReentrantReadWriteLock(true).writeLock();

public void addEle(Integer ele) {
    
    
writeLock.lock(); // 获取写锁
arrayList.add(ele);
writeLock.unlock(); // 释放写锁
}
public Integer getEle(Integer index) {
    
    
try{
    
    
readLock.lock(); // 获取读锁
Integer res = arrayList.get(index);
return res;
} finally{
    
    
readLock.unlock();// 释放读锁
}
}
}

1.2 The attributes and methods in the Sync source code have been discussed in the previous article

2. Get the write lock source code analysis

The lock method in ReentrantReadWriteLock

public void lock() {
    
    
sync.acquire(1);
}

The acquire method in AbstractQueuedSynchronizer

public final void acquire(int arg) {
    
    
    // 获取锁失败则进入阻塞队列
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}

The acquireQueued method and addWaiter method in acquireQueued(addWaiter(Node.EXCLUSIVE), arg))****, have been explained in detail in the previous article.
Insert picture description here

The tryAcquire method in ReentrantReadWriteLock

protected final boolean tryAcquire(int acquires) {
    
    
    // 获取当前线程
    Thread current = Thread.currentThread();
    //  获取状态
    int c = getState();
    // 计算写线程数量就是独占锁的可从入数量
    int w = exclusiveCount(c);
    // 当前同步状态state != 0,说明已经有其他线程获取了读锁或写锁
    if (c != 0) {
    
    
       // 当前state不为0,此时:如果写锁状态为0说明读锁此时被占用返回false;
       // 如果写锁状态不为0且写锁没有被当前线程持有返回false
        if (w == 0 || current != getExclusiveOwnerThread())
            return false;
       // 判断同一线程获取写锁是否超过最大次数(65535),支持可重入     
        if (w + exclusiveCount(acquires) > MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
         //更新状态
         //此时当前线程已持有写锁,现在是重入,所以只需要修改锁的数量即可
        setState(c + acquires);
        return true;
    }
      //到这里说明此时c=0,读锁和写锁都没有被获取
      //writerShouldBlock表示是否阻塞
    if (writerShouldBlock() ||
        !compareAndSetState(c, c + acquires))
        return false;
        // 设置锁为当前线程所有
    setExclusiveOwnerThread(current);
    return true;
}
static final class FairSync extends Sync {
    
    
// 写锁是否应该被阻塞
    final boolean writerShouldBlock() {
    
    
return hasQueuedPredecessors();
}
}

3. Flowchart for acquiring write lock

3.1 Flow chart of the process of acquiring a write lock

Insert picture description here

3.2 Flow chart analysis of the process of acquiring a write lock

The process of obtaining a write lock is as follows:

  1. First get c and w. c represents the current lock status; w represents the number of write threads. Then determine whether the synchronization state state is 0. If state!=0, it means that other threads have acquired a read lock or write lock.
  2. If the lock status is not zero (c != 0), and the write lock status is 0 (w = 0), it means that the read lock is occupied by other threads at this time, so the current thread cannot acquire the write lock, and naturally returns false. Or the lock status is not zero, and the write lock status is not 0, but the thread acquiring the write lock is not the current thread, the current thread cannot acquire the write lock either.
  3. Judge whether the current thread has acquired the write lock more than the maximum number of times, if it exceeds, throw an exception, otherwise update the synchronization state (the current thread has acquired the write lock, and the update is thread-safe), and return true.
  4. If the state is 0, neither the read lock nor the write lock is acquired at this time, and it is judged whether it needs to be blocked (fair and unfair methods are implemented differently). Under the unfair strategy, it will not be blocked. Under the fair strategy, it will be judged. (Judging whether there is a thread with a longer waiting time in the synchronization queue, if it exists, it needs to be blocked, otherwise, it does not need to be blocked), if it does not need to be blocked, the CAS updates the synchronization state, if the CAS succeeds, it returns true, and if it fails, it means the lock Snatched by another thread, return false. It also returns false if it needs to be blocked.
  5. After successfully acquiring the write lock, set the current thread as the thread that holds the write lock and return true.
  6. If the lock acquisition fails, the current thread is put into the blocking queue.

4. Release write lock source code analysis

The unlock method in ReentrantReadWriteLock

public void unlock() {
    
    
    sync.release(1);
}

The release method in AbstractQueuedSynchronizer

public final boolean release(int arg) {
    
    
    // 如果返回true 那么释放成功了
    if (tryRelease(arg)) {
    
    
        Node h = head;
        // 如果头部不为空,并且头节点的waitStatus是唤醒状态那么唤醒后继线程
        if (h != null && h.waitStatus != 0)
         // 唤醒后继线程
            unparkSuccessor(h);
        return true;
    }
    return false;
}

The tryRelease method in ReentrantReadWriteLock

protected final boolean tryRelease(int releases) {
    
    
// 若锁的持有者不是当前线程,抛出异常
    if (!isHeldExclusively())
     // 非法的监控器异常
        throw new IllegalMonitorStateException();
        // 计算写锁的新线程数
    int nextc = getState() - releases;
    // 如果独占模式重入数为0了,说明独占模式被释放
    boolean free = exclusiveCount(nextc) == 0;
    if (free)
     // 设置独占线程为空
        setExclusiveOwnerThread(null);
    // 设置写锁的新线程数
    // 不管独占模式是否被释放,更新独占重入数
    setState(nextc);
    return free;
}
protected final boolean isHeldExclusively() {
    
    
    // 若当前线程是当前锁的持有线程那么返回true
    return getExclusiveOwnerThread() == Thread.currentThread();
}

5. Flowchart of Release Write Lock

5.1 Flow chart release process

Insert picture description here

5.2 Analysis of flow chart release process

The release process of the write lock:

  1. First check whether the current thread is the holder of the write lock, if not, throw an exception. Then check whether the number of threads that write the lock after release is 0, if it is 0, it means that the write lock is free, release the lock resource and set the lock holding thread to null, otherwise the release is only a reentry lock, and the write cannot be written The locked thread is emptied.
  2. Description: This method is used to release write lock resources. First, it will determine whether the thread is an exclusive thread. If it is not an exclusive thread, an exception will be thrown. Otherwise, the number of write locks after the resource is released is calculated. If it is 0, it means success. Release, the resource will not be occupied, otherwise, it means that the resource is still occupied.

6. Summary

6.1 state analysis

private volatile int state;

The int type occupies 4 bytes and 8 bits per byte, so the state is a 32-bit, the upper 16 bits represent the read lock and the lower 16 bits represent the write lock.

// 0x0000FFFF 16 进制
// 1111111111111111 2 进制
// 65535 10 进制
static final int SHARED_SHIFT   = 16;
static final int SHARED_UNIT    = (1 << SHARED_SHIFT); // 65536
static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1; //65535  
// 1111111111111111
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; // 65535 
// 1111111111111111

If the synchronization status bit c at this time, then get the write status c & EXCLUSIVE_MASK
If the synchronization status bit c at this time, then get the read status c >>>16 unsigned 0, shift 16 bits to the right

6.2 Attention

The above is the analysis of the write lock in ReentrantReadWriteLock. The next article will be the analysis of Condition ***. If there are any errors, please help me to correct them in time. Thank you, *** If you like, thank you, like, add to favorites and forward (forward Thank you for indicating the source!!!)

Guess you like

Origin blog.csdn.net/weixin_38071259/article/details/112988612