In-depth understanding of AQS's ReentrantReadWriteLock detailed explanation

Table of contents

1. Introduction to ReentrantReadWriteLock

1.1 Reading, reading and writing, writing and reading, writing and writing in practice

1.2 Hashmap thread safety problem solving actual combat

1.3 Lock downgrade in practice

2. ReentrantReadWriteLock source code analysis (TODO)

3. stampedLock (TODO)


1. Introduction to ReentrantReadWriteLock

Application scenario: There is no concurrency problem in read and read operations, read and write, write and write, mutual exclusion is required in the read and write scenarios, and a lock is required to lock the required resources.

For the above scenario, java implements reentrantReadWriteLock, and implements lock downgrade (write lock to read lock) in java, fair and unfair selection, and reentrant.

Design how to implement a lock : Inherit AQS, implement the abstract methods tryRelease and tryAcquire methods, and other methods can use AQS already encapsulated.

Schedule:synchronized

AQS: cas+synchronous queue (obtaining lock failure blocking)

How to design a read-write lock: read-read, read-write, write-read, write-write lock design

Read lock: shared lock

Write lock: exclusive lock

In AQS, 0->1 in the state indicates that the lock has been acquired. In the read-write lock, the 32 bits modified by int can be divided. The upper 16 bits indicate the read lock, and the lower 16 bits indicate the write lock.

How to judge read lock and write lock: the upper 16 bits are not 0 or the lower 16 bits are not 0

1.1 Reading, reading and writing, writing and reading, writing and writing in practice

Except for reading and reading which are not mutually exclusive, the other three are mutually exclusive. Only when the first lock is released can the next lock be acquired. The demo code is as follows.

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockTest {


    //定义一个读写锁,并且定义一个读锁,一个写锁
    private ReadWriteLock lock = new ReentrantReadWriteLock();

    private Lock r = lock.readLock();
    private Lock w = lock.writeLock();

    //定义一个对象
    private Object data = "";

    //定义一个读方法
    public void read() {
        System.out.println(Thread.currentThread().getName()+"读锁-begin");
        r.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"获取读锁");
            data = "read";
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            r.unlock();
            System.out.println(Thread.currentThread().getName()+"读锁-解锁");
        }
    }

    //定义一个写方法
    public void write() {
        System.out.println(Thread.currentThread().getName()+"写锁-begin");
        w.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"获取写锁");
            data = "write";
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            w.unlock();
            System.out.println(Thread.currentThread().getName()+"写锁-解锁");
        }
    }

    public static void main(String[] args) {
        //测试读读,读写,写读,写写场景
        ReadWriteLockTest readWriteLockTest = new ReadWriteLockTest();
        new Thread(()->{
//            readWriteLockTest.read();
            readWriteLockTest.write();
        }).start();

        new Thread(()->{
            readWriteLockTest.read();
//            readWriteLockTest.write();
        }).start();
    }
}

1.2 Hashmap thread safety problem solving actual combat

Using ReadWriteLock to encapsulate hashMap again can achieve concurrent access security issues. This scenario is suitable for reading more and writing less.

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockTest2 {

    //定义一个hashMap和读写锁
    private static Map<String,Object> map = new HashMap<String,Object>();

    private static ReadWriteLock lock = new ReentrantReadWriteLock();

    private static Lock r = lock.readLock();
    private static Lock w = lock.writeLock();

    //获取时获取读锁
    public static Object get(String key){
        try{
            r.lock();
            return map.get(key);
        }finally {
            r.unlock();
        }
    }

    //插入时插入写锁
    public static void put(String key,Object value){
        try{
            w.lock();
            map.put(key,value);
        }finally {
            w.unlock();
        }
    }

    //清理写锁
    public void clear(){
        try{
            w.lock();
            map.clear();
        }finally {
            w.unlock();
        }
    }

    public static void main(String[] args) {
        ReadWriteLockTest2.put("key","value");
        System.out.println(ReadWriteLockTest2.get("key"));
    }
}

1.3 Lock downgrade in practice

Lock downgrade process: Write locks are converted to read locks. During the lock downgrade process, read locks are acquired mainly to ensure visibility.

Application scenario is unknown

public class ReadWriteLockTest3 {

    private static ReadWriteLock lock = new ReentrantReadWriteLock();

    private static Lock r = lock.readLock();
    private static Lock w = lock.writeLock();

    private static boolean flag = false;

    //锁降级过程:写锁降级为读锁,1.加读锁释放读锁 2.获取写锁中间加写锁释放写锁 3.try-finally释放读锁
    public static void update(){
        r.lock();
        if(!flag){
            r.unlock();
            w.lock();//锁降级过程从此时开始
            try{
                if(!flag){
                    flag = true;
                }
                r.lock();
            }finally {
                w.unlock();//锁降级完成,写锁转为读锁
            }
        }
        try{
            //锁降级完成,写锁降为读锁
        }finally {
            r.unlock();
        }
    }
}

2. ReentrantReadWriteLock source code analysis (TODO)

HoldCounter and ThreadLocalHoldCounter in ReentrantReadWriteLock are a counter. Use these two interfaces to store the number of acquired read locks. The number of reentrants exists in ThreadLocal

focus point:

1. How does the state store the status of read locks and write locks?

        Shared lock: the int value is shifted to the right by 16 bits (2 >>> 16)

        Exclusive lock: bit operation (int c & 65536)

2. Read lock and unlock

        Locking can be roughly divided into:

                1. Try to acquire the lock and judge that it is a write lock, return -1; read lock counter HoldCounter+1

                2. Enter the queue operation, create a queue, fail to acquire a lock and enter the synchronous queue blocking queue

        Unlock: Get the lock status value to judge the counter using CAS operation -- operation

3. Write lock and unlock (TODO)

 

 

3. stampedLock (TODO)

Guess you like

Origin blog.csdn.net/qq_21575929/article/details/124719182