浅谈ReentrantReadWriteLock


一、ReentrantReadWriteLock的作用是什么?

ReentrantReadWriteLock同样也是JUC包下面的一个并发工具类,它主要是用来给一个对象的读写操作上锁的。它对于读多写少的场景在性能上会更优于synchronized、ReentrantLock。

二、ReentrantReadWriteLock的读锁可重入,那么它是如何记录重录次数的?

ReentrantReadWriteLock的读锁是通过共享锁实现的,一般而言共享锁是无法重入的,因为共享锁的state属性在获取锁的时候只能减而不像独占锁那样可以进行累加。因而ReentrantReadWriteLock的读锁是通过另外一套逻辑实现的可重入的:第一次获取锁的线程会有独立的变量来存储该线程的重入次数,而非第一次进入的线程,ReentrantReadWriteLock会创建一个HoldCounter计数器来对该线程的冲入次数进行统计,并且会将之放入当前线程的ThreadLocal属性中来保证各个线程之间的统计独立。

三、ReentrantReadWriteLock如何实现通过一个int来分别给读写两种操作上锁的?

ReentrantReadWriteLock是利用了int类型在内存中占用32位的特性来实现的,它将前16位的高位作为读锁的记录位置,后16位低位作为写锁的记录位置,然后通过位运算来记录读写锁状态的变换。
这样的设计模式自然也有它的局限,一旦线程数量超过了65536(16位二进制表示的最大值)个的话上锁的操作就会失败

四、ReentrantReadWriteLock的底层是如何实现的?

ReentrantReadWriteLock中的读锁是通过共享锁的机制实现的,唯一的不同点就在于它可重入(原因之前有做解答);ReentrantReadWriteLock中的写锁是通过独占锁的方式实现的所以能保证写写之间的互斥;它优于ReentrantLock的地方就是读读操作没有互斥,所以读多写少的情况下ReentrantReadWriteLock的性能是更好的。

五、ReentrantReadWriteLock的锁降级是什么意思?为什么要使用它?

ReentrantReadWriteLock的锁降级其实就是写锁变为读锁的过程。这个过程其实是在写锁释放之前实现的,它保证了线程之间变量的可见性从而实现了线程安全。
数据写入主存中是需要一定的时间的,如果是自增操作的话,在未更新主存之前就读出主存中的数据进行自增并写入会造成脏读,导致线程安全问题,加上读锁是为了给刷新主存数据提供时间

六、ReentrantReadWriteLock的简单使用

public class ReadAndWriteLock {
    
    

    private Map<String, Object> map = new HashMap<>();

    private ReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();

    private Lock r = reentrantReadWriteLock.readLock();

    private Lock w = reentrantReadWriteLock.writeLock();

    private Object getMap(String key) {
    
    
        System.out.println("准备获取读锁~~~");
        r.lock();
        try {
    
    
            System.out.println(Thread.currentThread().getName() + ":获取数据成功!时间点为:" + System.currentTimeMillis());
            return map.get(key);
        } finally {
    
    
            r.unlock();
            System.out.println("释放读锁~~~");
        }
    }

    private void putMap(String key, Object obj) {
    
    
        System.out.println("准备获取写锁~~~");
        w.lock();
        try {
    
    
            map.put(key, obj);
            System.out.println(Thread.currentThread().getName() + ":插入数据成功!时间点为:" + System.currentTimeMillis());
        } finally {
    
    
            w.unlock();
            System.out.println("释放写锁~~~");
        }
    }

    public static void main(String[] args) {
    
    

        ReadAndWriteLock readAndWriteLock = new ReadAndWriteLock();

        new Thread(() -> {
    
    
            readAndWriteLock.putMap("xmx", "你好哈!");
        }).start();

        new Thread(() -> {
    
    
            readAndWriteLock.getMap("xmn");
        }).start();
    }

}

上述代码中,两个线程之间的读写业务可替换组合,规律如下:

  • 读读并行、读写互斥、写读互斥、写写互斥

猜你喜欢

转载自blog.csdn.net/qq_42697271/article/details/121565889
今日推荐