Java读写锁(ReentrantReadWriteLock)学习

什么是读写锁

平时,我们常见的synchronized和Reentrantlock基本上都是排他锁,这些锁在同一时刻只允许一个线程进行访问,哪怕是读操作。而读写锁是维护了一对锁(一个读锁和一个写锁),通过分离读锁和写锁,使得同一时刻可以允许多个读线程访问,但是在写线程进行访问时,所有的读线程和其他写线程均被阻塞。

读写锁的优点

1. 简化了读写交互场景编程的复杂度:

在常见的开发中,我们经常会定义一个共享的用作缓存的数据结构;比如一个大Map,缓存全部的城市Id和城市name对应关系。这个大Map绝大部分时间提供读服务(根据城市Id查询城市名称等);而写操作占有的时间很少,通常是在服务启动时初始化,然后可以每隔一定时间再刷新缓存的数据。但是写操作开始到结束之间,不能再有其他读操作进来,并且写操作完成之后的更新数据需要对后续的读服务可见。

在没有读写锁支持的时候,如果需要完成上述工作就要使用Java的等待通知机制,就是当写操作开始时,所有晚于写操作的读操作均会进入等待状态,只有写操作完成并进行通知之后,所有等待的读操作才能继续执行(多个写操作之间依靠synchronized关键字进行同步),这样做的目的是使读操作能读取到正确的数据,不会出现脏读。改用读写锁实现上述功能,这只需要在读操作时获取读锁,写操作时获取写锁即可。当写锁被获取到时,后续(非当前操作线程)的读写锁都会被阻塞,写锁释放之后,所有操作继续执行,编程方式相对于使用等待通知机制的实现方式而言。变得简单明了。

2.扩大了程序的吞吐量:

读写锁通过读写使用不同的锁,读操作使用共享锁,写操作使用独占锁的形式,让程序能提供更好的并发性和吞吐量。在读多于写的情况下,读写锁能够提供比排它锁更好的性能。

ReentrantReadWriteLock的特性

特性 说明
公平性选择 支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平
重进入 该锁支持重进入,以读写线程为例:读线程在获取了读锁之后,能够再次获取读锁。而写线程在获取了写锁之后能够再次获取写锁,同时也可以获取读锁
锁降级 允许从写锁降级为读锁,实现方式时:先获取写锁,再获取读锁,最后释放写锁,此时就会将为写锁。但是,从读锁升级到写锁时不可能的
Condition支持 写锁提供了一个Condition实现,对于写锁来说,该实现的行为与ReentrantLock.newCondition()提供的Condition实现对ReentrantLock所做的行为相同。当然,此Condition只能用于写锁。读锁不支持Condition,readLok().newCondition()会抛出UnsupportedOperationException。
监测 这些方法主要用于监听系统状态,而不是同步控制

使用案例

public class ReentrantReadWriteLockTest {

    private static Map<String, Object> map = new HashMap<>();
    private static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private static Lock readLock = rwl.readLock();
    private static Lock writeLock = rwl.writeLock();

    public static void main(String[] args) {

        ReentrantReadWriteLockTest reentrantReadWriteLockTest = new ReentrantReadWriteLockTest() ;

        Executor executor = Executors.newFixedThreadPool(4) ;
        executor.execute(() -> {
            System.out.println("写锁开始");
            reentrantReadWriteLockTest.put("1","1") ;
            System.out.println("写锁结束");
        });
        executor.execute(() -> {
            System.out.println("读锁开始");
            System.out.println(reentrantReadWriteLockTest.get("4"));
            System.out.println("读锁结束");
        });
        executor.execute(() -> {
            System.out.println("写锁开始");
            reentrantReadWriteLockTest.put("3", "3") ;
            System.out.println("写锁开始");
        });
        executor.execute(() -> {
            System.out.println(reentrantReadWriteLockTest.get("1"));
        });
    }

    public void put(String key , String value) {
        try {
            writeLock.lock();
            map.put(key, value);
        }finally {
            writeLock.unlock();
        }
    }

    public Object get(String key) {
        try {
            readLock.lock();
            return map.get(key);
        }finally {
            readLock.unlock();
        }
    }
}
时间: 2024-10-14 18:17:03

Java读写锁(ReentrantReadWriteLock)学习的相关文章

[图解Java]读写锁ReentrantReadWriteLock

图解ReentrantReadWriteLock 如果之前使用过读写锁, 那么可以直接看本篇文章. 如果之前未使用过, 那么请配合我的另一篇文章一起看:[源码分析]读写锁ReentrantReadWriteLock 0. demo 我先给出一个demo, 这样大家就可以根据我给的这段代码, 边调试边看源码了. 还是那句话: 注意"My" , 我把ReentrantReadWriteLock类 改名为了 "MyReentrantReadWriteLock"类 , &q

Java读写锁ReentrantReadWriteLock的探索与应用

利用加了写锁后,将阻塞后续尝试加写锁的线程的特性,为写入用户历史动作增加写锁,以达到在写过程中不受其他线程影响的效果,来保证每个用户只能领取一次金额.参考代码如下: package com.miqtech.test.readwritelock; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Random; import java.util.conc

读写锁 ReentrantReadWriteLock

一.读写锁 ReadWriteLock概念特点读写锁维护了一对相关的锁,一个用于只读操作,一个用于写入操作.只要没有writer,读取锁可以由多个reader线程同时保持.写入锁是独占的. 互斥锁[ReetrantLock]一次只允许一个线程访问共享数据,哪怕进行的是只读操作:读写锁[ReadWriteLock]允许对共享数据进行更高级别的并发访问:对于写操作,一次只有一个线程(write线程)可以修改共享数据,对于读操作,允许任意数量的线程同时进行读取.writer可以获取读取锁,但reade

Java读-写锁

显示锁 在java5.0之前,在协调共享对象访问时可以使用的机制只有synchronized和volatile.java5.0增加了一种新的机制:ReentrantLock.ReentrantLock并不是一种替代内置锁的方法,而是当内置锁不适用时,作为一种可选择的高级功能.与内置锁不同的是Lock提供了一个无条件的.可轮询的.定时的以及可中断的锁获取操作,所有加锁和解锁都是显示的.在Lock的实现中必须提供与内部锁相同的内存可见性语义. /**Lock接口*/ public interface

被面试官吊打系列之JUC之 可重入读写锁ReentrantReadWriteLock 之 源码详尽分析

可重入读写锁 ReentrantReadWriteLock 其实基本上模拟了文件的读写锁操作.ReentrantReadWriteLock 和ReentrantLock 的差别还是蛮大的: 但是也有很多的相似之处: ReentrantReadWriteLock 的 writerLock 其实就是相当于ReentrantLock,但是它提供更多的细腻的控制:理解什么是读锁.写锁非常重要,虽然实际工作中区分读写锁这样的细分使用场景比较少. ReentrantReadWriteLock 把锁进行了细化

深入浅出 Java Concurrency (13): 锁机制 part 8 读写锁 (ReentrantReadWriteLock) (1)[转]

从这一节开始介绍锁里面的最后一个工具:读写锁(ReadWriteLock). ReentrantLock 实现了标准的互斥操作,也就是一次只能有一个线程持有锁,也即所谓独占锁的概念.前面的章节中一直在强调这个特点.显然这个特点在一定程度上面减低了吞吐量,实际上独占锁是一种保守的锁策略,在这种情况下任何“读/读”,“写/读”,“写/写”操作都不能同时发生.但是同样需要强调的一个概念是,锁是有一定的开销的,当并发比较大的时候,锁的开销就比较客观了.所以如果可能的话就尽量少用锁,非要用锁的话就尝试看能

深入浅出 Java Concurrency (14): 锁机制 part 9 读写锁 (ReentrantReadWriteLock) (2)[转]

这一节主要是谈谈读写锁的实现. 上一节中提到,ReadWriteLock看起来有两个锁:readLock/writeLock.如果真的是两个锁的话,它们之间又是如何相互影响的呢? 事实上在ReentrantReadWriteLock里锁的实现是靠java.util.concurrent.locks.ReentrantReadWriteLock.Sync完成的.这个类看起来比较眼熟,实际上它是AQS的一个子类,这中类似的结构在CountDownLatch.ReentrantLock.Semapho

深入浅出 Java Concurrency (13): 锁机制 part 8 读写锁 (ReentrantReadWriteLock) (1)

从这一节开始介绍锁里面的最后一个工具:读写锁(ReadWriteLock). ReentrantLock 实现了标准的互斥操作,也就是一次只能有一个线程持有锁,也即所谓独占锁的概念.前面的章节中一直在强调这个特点.显然这个特点在一定程度上面减低了吞吐量,实际上独占锁是一种保守的锁策略,在这种情况下任何"读/读","写/读","写/写"操作都不能同时发生.但是同样需要强调的一个概念是,锁是有一定的开销的,当并发比较大的时候,锁的开销就比较客观了.所

深入浅出 Java Concurrency (14): 锁机制 part 9 读写锁 (ReentrantReadWriteLock) (2)

这一节主要是谈谈读写锁的实现. 上一节中提到,ReadWriteLock看起来有两个锁:readLock/writeLock.如果真的是两个锁的话,它们之间又是如何相互影响的呢? 事实上在ReentrantReadWriteLock里锁的实现是靠java.util.concurrent.locks.ReentrantReadWriteLock.Sync完成的.这个类看起来比较眼熟,实际上它是AQS的一个子类,这中类似的结构在CountDownLatch.ReentrantLock.Semapho