JUC并发编程之读写锁

news2025/1/21 0:53:14

1 ReentrantReadWriteLock

当读操作远远高于写操作,这时候使用 读写锁读-读 可以并发,提高性能,类似于数据库中的 select … from … lock in share mode

测试阻塞

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

import com.lv.juc.util.Sleeper;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author 晓风残月Lx
 * @date 2023/4/7 11:07
 */
@Slf4j
public class MyReadWriteLockTest01 {

    public static void main(String[] args) {
        DataContainer dataContainer = new DataContainer();
        new Thread(() -> {
            dataContainer.read();
        }, "t1").start();

        new Thread(() -> {
            dataContainer.read();
        }, "t2").start();

    }
}

@Slf4j
class DataContainer {
    private Object data;
    private ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
    private ReentrantReadWriteLock.ReadLock r = rw.readLock();
    private ReentrantReadWriteLock.WriteLock w = rw.writeLock();


    public Object read() {
        log.debug("获取读锁.....");
        r.lock();

        try {
            log.debug("读取");
            Sleeper.sleep(1);
            return data;
        } finally {
            log.debug("释放读锁");
            r.unlock();
        }
    }

}

在这里插入图片描述

测试 读锁-写锁 相互阻塞

@Slf4j
public class MyReadWriteLockTest01 {

    public static void main(String[] args) {
        DataContainer dataContainer = new DataContainer();
        new Thread(() -> {
            dataContainer.read();
        }, "t1").start();

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

    }

在这里插入图片描述

写锁-写锁 阻塞

@Slf4j
public class MyReadWriteLockTest01 {

    public static void main(String[] args) {
        DataContainer dataContainer = new DataContainer();
        new Thread(() -> {
            dataContainer.write();
        }, "t1").start();

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

    }
}

在这里插入图片描述

注意事项

  • 读锁不支持条件变量
  • 重入时升级不支持:即持有读锁的情况下去获取写锁,会导致获取写锁永久等待
    r.lock();
	try {
        // ...
        w.lock();
        try {
            // ...
        } finally{
            w.unlock();
        }
    } finally{
        r.unlock();
    }
  • 重入时降级支持:即持有写锁的情况下获取读锁
class CachedData {
    Object data;
    // 是否有效,如果失效,需要重新计算 data
    volatile boolean cacheValid;
    final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    void processCachedData() {
        rwl.readLock().lock();
        if (!cacheValid) {
            // 获取写锁前必须释放读锁
            rwl.readLock().unlock();
            rwl.writeLock().lock();
            try {
                // 判断是否有其它线程已经获取了写锁、更新了缓存, 避免重复更新
                if (!cacheValid) {
                    data = ...
                    cacheValid = true;
                }
                // 降级为读锁, 释放写锁, 这样能够让其它线程读取缓存
                rwl.readLock().lock();
            } finally {
                rwl.writeLock().unlock();
            }
        }
        // 自己用完数据, 释放读锁 
        try {
            use(data);
        } finally {
            rwl.readLock().unlock();
        }
    }
}
            

2.StampedLock

该类是JDK 8 加入,是为了进一步优化读性能,特点是在使用读锁、写锁时都必须配合【戳】使用

加解读锁

long stamp = lock.readLock();
lock.unlockRead(stamp);

加解写锁

long stamp = lock.writeLock();
lock.unlockWrite(stamp);

乐观读,StampedLock 支持 tryOptimisticRead 方法(乐观读),读取完毕后需要做一次 戳校验,如果校验通过,表示这期间确实没有写操作,数据可以安全使用,如果检验没通过,需要重新获取读锁,保证数据安全

long stamp = lock.tryOptimisticRead();
// 验戳
if(!lock.validate(stamp)) {
    // 锁升级
}

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

import com.lv.juc.util.Sleeper;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.StampedLock;

/**
 * @author 晓风残月Lx
 * @date 2023/4/14 8:56
 */
@Slf4j
public class DataContainerStamped {

    private int data;

    private final StampedLock lock = new StampedLock();

    public DataContainerStamped(int data) {
        this.data = data;
    }

    public int read(int readTime) {
        long stamp = lock.tryOptimisticRead();
        log.debug("optimistic read locking.... {}", stamp);
        Sleeper.sleep(readTime);
        if (lock.validate(stamp)) {
            log.debug("read finish .... {}, data:{}", stamp, data);
            return data;
        }

        // 锁升级 - 读锁
        log.debug("updating to read lock...{}", stamp);
        try {
            stamp = lock.readLock();
            log.debug("read lock{}", stamp);
            Sleeper.sleep(readTime);
            log.debug("read finish...{},data:{}", stamp, data);
            return data;
        } finally {
            log.debug("read unlock ... {}", stamp);
            lock.unlockRead(stamp);
        }
    }

    public void write(int newData) {
        long stamp = lock.writeLock();
        log.debug("write lock {}", stamp);
        try {
            Sleeper.sleep(2);
            this.data = newData;
        } finally {
            log.debug("write unlock ... {}", stamp);
            lock.unlockWrite(stamp);
        }
    }

}

测试 读读并发

public class DataContainerStampedTest {

    public static void main(String[] args) {
        DataContainerStamped dataContainerStamped = new DataContainerStamped(1);
        new Thread(() -> {
            dataContainerStamped.read(1);
        },"t1").start();

        Sleeper.sleep(0.5);
        new Thread(() -> {
            dataContainerStamped.read(0);
        }, "t2").start();
    }
}

在这里插入图片描述

测试 读写 优化读补加读锁

import com.lv.juc.util.Sleeper;
import lombok.extern.slf4j.Slf4j;

/**
 * @author 晓风残月Lx
 * @date 2023/4/14 9:26
 */
@Slf4j
public class DataContainerStampedTest01 {

    public static void main(String[] args) {
        DataContainerStamped dataContainerStamped = new DataContainerStamped(1);
        new Thread(() -> {
            dataContainerStamped.read(1);
        },"t1").start();

        Sleeper.sleep(0.5);
        new Thread(() -> {
            dataContainerStamped.write(1000);
        }, "t2").start();
    }
}

在这里插入图片描述

  • StampedLock 不支持条件变量
  • StampedLock 不支持可重入

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/434569.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NAT-HCIA阶段综合实验

拓扑结构: 要求 1、ISP路由器只能配置IP地址,之后不得进行其他配置 2、内部整个网络基于192.168.1.0/24进行地址规划 3、R1、R2之间启动OSPF协议,单区域 4、PC1~PC4自动获取IP地址 5、PC1不能telnetR1,PC1外的其他内网PC可以t…

设计模式-行为型模式之状态模式

4. 状态模式 4.1. 模式动机 在很多情况下,一个对象的行为取决于一个或多个动态变化的属性,这样的属性叫做状态,这样的对象叫做有状态的(stateful)对象,这样的对象状态是从事先定义好的一系列值中取出的。当一个这样的对象与外部事…

GPT大模型之后,谷歌、微软、百度们AI争霸的下一战

又来了一个大模型! 4 月 14 日,雷军宣布推出小米的大规模语言模型;4 月 11 日,在阿里云峰会上,阿里云智能首席技术官周靖人正式宣布推出大规模语言模型——通义千问,并宣布要在钉钉、天猫精灵等阿里所有产品…

Ghidra使用之Options页面功能介绍

Apply Processor Defined Lables 在Ghidra中,apply processor defined labels功能可以为汇编代码中的地址和数据自动添加注释,这可以大大提高反汇编代码的可读性。 使用步骤如下: 打开您要反汇编的文件,进入Code Browser窗口。点击Edit菜单,选择Apply Processor Defined Labels…

Mysql不同服务器跨库查询解决方案

项目场景: Mysql在不同服务器实现跨库查询,类似dblink。 解决方案: 在两台不同服务器,实现跨库查询,其实现原理类似一个虚拟映射,需要用到mysql的另一个存储引擎Federated,FEDERATED存储引擎访问在远程数据…

另一个世界的人

休假 赶在假期无效前,休了个长假回了趟老家。不仅是为了逃离工作的繁忙,也为了看望老家的婆婆爷爷。回趟老家对于我们这种社会人来说确实太难了,如果只是周末回去也就休息一天就得往回赶,太累。自己的假期本来就不多,如…

如何对HDFS进行节点内(磁盘间)数据平衡

当HDFS的DataNode节点挂载多个磁盘时,往往会出现两种数据不均衡的情况: 1.不同DataNode节点间数据不均衡; 2.挂载数据盘的磁盘间数据不均衡。 特别是这种情况:当DataNode原来是挂载了几个数据盘,当磁盘占用率很高之…

7.1 参数的点估计

小结: 点估计是一种统计推断方法,它用于通过样本数据估计总体参数的值。在统计学中,总体是指一个包含所有个体的集合,而样本是从总体中选出的一部分个体。总体参数是总体的某种特征,如平均值、标准差、比例等。 点估…

全志V851s、V853内g2d模块sample深究

1. g2d 模块概述 g2d 主要功能: 1)旋转:支持90、180、270旋转; 2)镜像反转:H / V; 3) scale:放缩 4)格式转换:yuv 转 rgb 等,多种格式相互间转换&#xff1b…

python学习——缺失值、重复值处理、排序及替换

文章目录 1 缺失值处理1.1 查看缺失值 df.isnull()1.2 统计缺失值 df.isnull().sum()1.3 删除缺失值 df.drop()1.4 填充缺失值 df.fillna()1.4.1 固定值填充 df.fillna(value)1.4.2 线性插值填充 df.fillna(df.interpolate()) 2 重复值处理2.1 查看重复值 df.duplicated()2.2 筛…

最佳实践:Android应用中的网络请求和数据缓存

最佳实践:Android应用中的网络请求和数据缓存 网络请求在Android应用中的重要性 在现代移动应用中,网络请求扮演着重要的角色,涉及到数据的获取、上传、更新等功能。网络请求在Android应用中具有关键地位,对于提供优秀的用户体验和…

如何使用山海鲸可视化绘制堆叠双轴图?

简介 堆叠双轴图是一种数据可视化图表类型,它结合了堆叠柱状图和双轴图的特点,将两个或更多个数据系列以不同的颜色或图案堆叠在一起,同时使用两个垂直轴来表示不同的数据范围,以显示它们之间的关系和趋势。其中一个轴通常代表数…

一个实例讲解如何使用BP神经网络(附代码)

本站原创文章,转载请说明来自《老饼讲解-BP神经网络》bp.bbbdata.com BP神经网络是一个广泛应用的模型, 本文以一个实例作为主线,面向初学者讲解如何构建和使用一个BP神经网络 帮助没接触进BP神经网络的初学者,快速上手BP神经网络…

传统机器学习(四)聚类算法DBSCAN

传统机器学习(四)聚类算法DBSCAN 1.1 算法概述 DBSCAN(Density-Based Spatial Clustering of Applications with Noise,具有噪声的基于密度的聚类方法)是一种基于密度的空间聚类算法。 该算法将具有足够密度的区域划分为簇,并在…

【C++学习】类和对象--对象特性(2)

静态成员 静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员。 静态成员变量: 所有对象共享同一份数据 在编译阶段分配内存 类内声明,类外初始化 静态成员函数: 所有对象共享同一个函数 静态成员函数只能访问静态…

MyBatis小技巧

一、MyBatis中接口代理机制及使用 我们不难发现,以前编写dao/mapper实现类中的方法代码很固定,基本上就是一行代码,通过SqlSession对象调用insert、delete、update、select等方法,这个类中的方法没有任何业务逻辑,既然…

【Cadence】 ADS Dynamic Link使用教程

ADS Dynamic Link使用教程 1.新建一个Cadence Schematic2.打开ADS3.在ADS新建Schematic4.加入控件OPTIONS5.加入Netlist6.仿真测试7.写在最后 1.新建一个Cadence Schematic 随便搭了一个Cascode 注意这个时候不要新建symbol 2.打开ADS 3.在ADS新建Schematic 选择对应的Cade…

BI技巧丨计算组环形图

当我们使用Power BI时,会发现其内置了许多可视化组件,这些组件可以帮我们快速地创建各种类型的报表和仪表板,这是非常方便的。但是,我们也会发现,有些组件的细节功能并不是很完善,这就导致了我们在平常使用…

解决 mapper.xml 文件的 resultType 爆红问题:Cannot resolve symbol ‘xxx‘

解决mapper.xml文件的resultType爆红问题:Cannot resolve symbol xxx 1.问题描述2.问题分析3.问题解决3.1 配置注解(推荐)3.2 配置全类名3.3 删除插件 4.事件感悟 系统:Win10 JDK:1.8.0_333 IDEA:2022.2.4 …

【C++】Qt环境下的五子棋小程序

实现要求 这个程序是一个基于Qt的五子棋游戏,实现了人机对战和双人对战两种游戏模式,提供了基本的棋谱记录和游戏音效功能。程序采用了MVC模式,将数据与界面分离,实现了代码的模块化和可扩展性。主要构建包括三个文件&#xff0c…