深入理解AQS之ReentrantReadWriteLock详解

目录

1.ReentrantReadWriteLock介绍

1.1 读读,读写,写读,写写实战

1.2 hashmap线程安全问题解决实战

1.3 锁降级实战

2. ReentrantReadWriteLock源码分析(TODO)

3. stampedLock (TODO)


1.ReentrantReadWriteLock介绍

应用场景:读读操作不存在并发问题,读写,写写,写读场景下需要互斥,需要一把锁锁住需要的资源。

针对上述场景java实现了reentrantReadWriteLock,并且在java中实现了锁降级(写锁到读锁),公平与非公平选择,可重入。

设计一把锁如何实现:继承AQS,实现抽象方法tryRelease和tryAcquire方法,其他方法可以使用AQS已经封装好的。

管程:synchronized

AQS:cas+同步队列(获取锁失败阻塞)

设计一把读写锁如何实现:读读,读写,写读,写写锁设计

读锁:共享锁

写锁:独占锁

AQS中通过state中0->1表示获取到锁,读写锁中可以将int修饰的32位切分,高16位表示读锁,低16位表示写锁。

如何判断读锁和写锁:高16位不为0或低16位不为0

1.1 读读,读写,写读,写写实战

除了读读不互斥以外,其他三种都互斥,只有释放第一个锁才能获取下一个锁,演示代码如下。

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线程安全问题解决实战

使用ReadWriteLock对hashMap进行再一次的封装,可以实现并发访问安全问题。这种场景适合读多写少。

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 锁降级实战

锁降级过程:写锁转为读锁,锁降级过程中读锁的获取主要是为了保证可见性。

应用场景不明

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源码分析(TODO)

ReentrantReadWriteLock中HoldCounter和ThreadLocalHoldCounter是一个计数器。用这两个接口来存储获取读锁的个数。可重入次数存在在ThreadLocal中

关注点:

1. state如何存储读锁和写锁的状态

        共享锁:int值往右移16位(2 >>> 16)

        独占锁:位运算(int c & 65536)

2.读锁加解锁

        加锁大致分为:

                1.尝试获取锁判断是写锁,返回-1;读锁计数器HoldCounter+1

                2. 入队操作,创建队列,获取锁失败进入同步队列阻塞队列

        解锁:获取锁状态值判断将计数器使用CAS操作--操作

3.写锁加解锁(TODO)

 

3. stampedLock (TODO)

猜你喜欢

转载自blog.csdn.net/qq_21575929/article/details/124719182