当前位置: 首页 > news >正文

ReentrantLock读写锁

ReentrantLock读写锁

ReentrantReadWriteLock

基本介绍

当读操作远远高于写操作时,这时候使用 读写锁 让 读-读 可以并发,提高性能。 类似于数据库中的 select ... from ... lock in share mode
提供一个数据容器类DataContainer内部分别使用读锁保护数据的 read() 方法,写锁保护数据的 write() 方法

需要注意的是 :

  • 读-读 是可以并发执行的
  • 写-写读-写 以及 写-读 都是互相阻塞的

读写锁的使用

DataContainer

提供一个 数据容器类 内部分别使用读锁保护数据的 read() 方法,写锁保护数据的 write() 方法

package cn.knightzz.juc.reentrantlock.readwrite;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author 王天赐
 * @title: DataContainer
 * @projectName hm-juc-codes
 * @description:
 * @website <a href="http://knightzz.cn/">http://knightzz.cn/</a>
 * @github <a href="https://github.com/knightzz1998">https://github.com/knightzz1998</a>
 * @create: 2022-09-19 15:23
 */
@SuppressWarnings("all")
@Slf4j(topic = "c.DataContainer")
public class DataContainer {

    private Object data;
    private ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
    private ReentrantReadWriteLock.ReadLock readLock = rw.readLock();
    private ReentrantReadWriteLock.WriteLock writeLock = rw.writeLock();

    public Object read() {

        // 开启读锁
        log.debug("获取读取锁 ... ");
        readLock.lock();
        try {
            log.debug("开始读取 ... ");
            TimeUnit.SECONDS.sleep(1);
            return data;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            log.debug("释放读锁 ... ");
            readLock.unlock();
        }
    }

    public void write() {
        log.debug("获取写入锁..");
        writeLock.lock();
        try {
            log.debug("开始写入数据 ... ");
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            log.debug("释放写入锁 ... ");
            writeLock.unlock();
        }
    }

}

ReadWriteLockTest

package cn.knightzz.juc.reentrantlock.readwrite;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.io.IOException;

/**
 * @author 王天赐
 * @title: ReadWriteLockTest
 * @projectName hm-juc-codes
 * @description:
 * @website <a href="http://knightzz.cn/">http://knightzz.cn/</a>
 * @github <a href="https://github.com/knightzz1998">https://github.com/knightzz1998</a>
 * @create: 2022-09-19 14:37
 */
@SuppressWarnings("all")
@Slf4j(topic = "c.ReadWriteLockTest")
public class ReadWriteLockTest {

    public static void main(String[] args) throws IOException, InterruptedException {
        log.debug("==========================={}=========================", "测试读锁并发");
        // 只有读锁是可以多个线程同时执行的
        DataContainer container = new DataContainer();
        for (int i = 1; i < 5; i++) {
            new Thread(() -> {
                container.read();
            }, "t" + i).start();
        }

        System.in.read();

        log.debug("==========================={}=========================", "测试写锁并发");
        // 写入 和 读写都是相互阻塞的, 只有一方释放锁, 另一方才可以获取对应的锁去执行
        for (int i = 1; i < 5; i++) {
            new Thread(() -> {
                container.write();
            }, "t" + i).start();
        }

        System.in.read();

        log.debug("==========================={}=========================", "测试读写锁相互阻塞");
        new Thread(() -> {
            container.read();
        }, "t1").start();

        // sleep 100ms
        Thread.sleep(100);

        new Thread(() -> {
            container.write();
        }, "t2").start();

        System.in.read();
    }
}

读并发测试 :

19:16:10.757 [main] DEBUG c.ReadWriteLockTest - ===========================测试读锁并发=========================
19:16:10.791 [t1] DEBUG c.DataContainer - 获取读取锁 ... 
19:16:10.792 [t2] DEBUG c.DataContainer - 获取读取锁 ... 
19:16:10.792 [t3] DEBUG c.DataContainer - 获取读取锁 ... 
19:16:10.792 [t3] DEBUG c.DataContainer - 开始读取 ... 
19:16:10.792 [t4] DEBUG c.DataContainer - 获取读取锁 ... 
19:16:10.792 [t1] DEBUG c.DataContainer - 开始读取 ... 
19:16:10.792 [t2] DEBUG c.DataContainer - 开始读取 ... 
19:16:10.792 [t4] DEBUG c.DataContainer - 开始读取 ... 
19:16:11.796 [t1] DEBUG c.DataContainer - 释放读锁 ... 
19:16:11.796 [t2] DEBUG c.DataContainer - 释放读锁 ... 
19:16:11.796 [t4] DEBUG c.DataContainer - 释放读锁 ... 
19:16:11.796 [t3] DEBUG c.DataContainer - 释放读锁 ... 

可以看到上面执行结果 : 开始读取的时间几乎是同一时间, 可以说明他们是同时获取锁的,

写并发测试 :

19:16:15.580 [main] DEBUG c.ReadWriteLockTest - ===========================测试写锁并发=========================
19:16:15.581 [t1] DEBUG c.DataContainer - 获取写入锁..
19:16:15.581 [t2] DEBUG c.DataContainer - 获取写入锁..
19:16:15.581 [t1] DEBUG c.DataContainer - 开始写入数据 ... 
19:16:15.581 [t3] DEBUG c.DataContainer - 获取写入锁..
19:16:15.581 [t4] DEBUG c.DataContainer - 获取写入锁..
19:16:16.587 [t1] DEBUG c.DataContainer - 释放写入锁 ... 
19:16:16.587 [t2] DEBUG c.DataContainer - 开始写入数据 ... 
19:16:17.602 [t2] DEBUG c.DataContainer - 释放写入锁 ... 
19:16:17.602 [t3] DEBUG c.DataContainer - 开始写入数据 ... 
19:16:18.606 [t3] DEBUG c.DataContainer - 释放写入锁 ... 
19:16:18.606 [t4] DEBUG c.DataContainer - 开始写入数据 ... 
19:16:19.610 [t4] DEBUG c.DataContainer - 释放写入锁 ... 

可以看到 t1 获取锁后开始写入数据, 此时有其他线程都在尝试获取锁, 但是都没有成功, 在t1释放锁后, t3线程获取到锁开始写入数据

读写并发

19:16:21.075 [main] DEBUG c.ReadWriteLockTest - ===========================测试读写锁相互阻塞=========================
19:16:21.076 [t1] DEBUG c.DataContainer - 获取读取锁 ... 
19:16:21.076 [t1] DEBUG c.DataContainer - 开始读取 ... 
19:16:21.185 [t2] DEBUG c.DataContainer - 获取写入锁..
19:16:22.085 [t1] DEBUG c.DataContainer - 释放读锁 ... 
19:16:22.085 [t2] DEBUG c.DataContainer - 开始写入数据 ... 
19:16:23.091 [t2] DEBUG c.DataContainer - 释放写入锁 ... 

可以看到上面的运行结果, t1线程先获取读锁, 此时t2线程也尝试获取写入锁, 但是等到1s后t1读取完成并释放锁后, t2线程才开始写入数据.

条件变量

  • 读锁不支持条件变量
  • 重入时升级不支持:即持有读锁的情况下去获取写锁,会导致获取写锁永久等待

ReentrantReadWriteLock应用

应用介绍

使用读写锁保证缓存一致性

相关文章:

  • leetcode 1680. Concatenation of Consecutive Binary Numbers(连接连续的二进制数)
  • Python数据分析之时间序列的处理
  • 【PHP】如何搭建服务器环境 原生篇 | Ubuntu 18.04 + PHP8.1 + MySQL5.7 + Nginx 1.4
  • 【c语言】数据在内存中的存储
  • 数据结构考试必须要掌握的重点知识
  • 进程管理4——进程优先级
  • 外网访问内网80端口【内网穿透】
  • Android性能优化技术,在大厂中为何这么看重?进大厂必学好
  • 基于自建数据集【海底生物检测】使用YOLOv5-v6.1/2版本构建目标检测模型超详细教程
  • 水平分表之基因法
  • Gorm笔记
  • 抽空做了个“胃肠镜”,唠唠嗑
  • 现在工作是不是很难找?
  • Colmap算法pipeline
  • QCC51XX---TwsTopology_Init(goals分析)
  • 2018以太坊智能合约编程语言solidity的最佳IDEs
  • 4个实用的微服务测试策略
  • C++11: atomic 头文件
  • echarts花样作死的坑
  • JavaScript设计模式系列一:工厂模式
  • leetcode98. Validate Binary Search Tree
  • React Transition Group -- Transition 组件
  • Transformer-XL: Unleashing the Potential of Attention Models
  • Web Storage相关
  • Web设计流程优化:网页效果图设计新思路
  • 回顾2016
  • 基于Vue2全家桶的移动端AppDEMO实现
  • 将 Measurements 和 Units 应用到物理学
  • 前端技术周刊 2018-12-10:前端自动化测试
  • 小程序上传图片到七牛云(支持多张上传,预览,删除)
  • 云大使推广中的常见热门问题
  • 正则表达式小结
  • 容器镜像
  • 如何在招聘中考核.NET架构师
  • ![CDATA[ ]] 是什么东东
  • ###C语言程序设计-----C语言学习(3)#
  • $jQuery 重写Alert样式方法
  • (java)关于Thread的挂起和恢复
  • (Oracle)SQL优化技巧(一):分页查询
  • .Net 8.0 新的变化
  • .NET CORE Aws S3 使用
  • .Net Framework 4.x 程序到底运行在哪个 CLR 版本之上
  • .net Stream篇(六)
  • .NET 解决重复提交问题
  • .net 设置默认首页
  • .NET/ASP.NETMVC 深入剖析 Model元数据、HtmlHelper、自定义模板、模板的装饰者模式(二)...
  • .net与java建立WebService再互相调用
  • [ SNOI 2013 ] Quare
  • [BZOJ1060][ZJOI2007]时态同步 树形dp
  • [CISCN2019 华北赛区 Day1 Web5]CyberPunk --不会编程的崽
  • [corCTF 2022] CoRJail: From Null Byte Overflow To Docker Escape
  • [LeetCode]剑指 Offer 42. 连续子数组的最大和
  • [MQ]常用的mq产品图形管理web界面或客户端
  • [oeasy]python0002_终端_CLI_GUI_编程环境_游戏_真实_元宇宙
  • [PTP][1588v2] Delay_Resp消息