ReadWriteLock可重入读写锁读写锁

简介: ReadWriteLock可重入读写锁读写锁

ReadWriteLock可重入读写锁读写锁

什么是ReadWriteLock读写锁?

它是一个接口,只有一个实现类:ReentrantReadWriteLock(可重复的读写锁,也叫可重入锁)

ReadWriteLock维护一对关联的lock,一个用于只读操作,一个用于写入操作

读的时候可以多个线程同时读,写的时候只能被一个线程写

读锁又叫共享锁 一次性只能被一个线程占用 相当于一个原子性操作

写锁又叫独占锁 多个线程可以同时占有

代码测试,自定义缓存,首先是没有加锁的情况下


package com.wyh.ReadWriteLockTest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
 * @program: JUC
 * @description: 读写锁测试
 * @author: 魏一鹤
 * @createDate: 2022-02-19 21:56
 **/
//ReadWriteLock是一个接口,只有一个实现类:ReentrantReadWriteLock(可重复的读写锁,也叫可重入锁)
//ReadWriteLock维护一对关联的lock 一个用于只读操作,一个用于写入操作,读的时候可以多个线程同时读,写的时候只能被一个线程写
//自定义缓存
public class ReadWriterLockDemo {
public static void main(String[] args){
//创建静态资源类
        MyCache myCache = new MyCache();
    //循环创建多线程 这几个线程只做读取的操作
        for (int i = 1; i <= 5; i++) {
 //在lambda表达式中无法直接使用循环变量i 需要定义常量指明变量i做一个中间转换
            //然后操作这个常量
            final  int temp=i;
new Thread(()->{
                myCache.put(temp+"",temp+"");
            }).start();
        }
             //循环创建多线程 这几个线程只做存储缓存的操作
             for (int i = 1; i <= 5; i++) {
                 //在lambda表达式中无法直接使用循环变量i 需要定义常量指明变量i做一个中间转换
                 //然后操作这个常量
                 final  int temp=i;
new Thread(()->{
                     myCache.get(temp+"");
                 }).start();
             }
    }
}
//没加锁
class MyCache{
//缓存中一般都是键值对 通过get set 或者put来存或者取
    //volatile保证原子性
    private volatile Map<String,Object> map=new HashMap<>();
    //存缓存 实际是一个写的过程
    public void put(String key,Object value){
        System.out.println(Thread.currentThread().getName()+"写入了"+key);
//键值对存缓存
        map.put(key,value);
        System.out.println(Thread.currentThread().getName()+"写入"+key+"完毕");
    }
   //取缓存 实际是一个读的过程
    public void get(String key){
        System.out.println(Thread.currentThread().getName()+"读取"+key);
//通过键取值
        Object o = map.get(key);
        System.out.println(Thread.currentThread().getName()+"读取"+key+"完毕");
    }
}


Thread-0写入了1

Thread-4写入了5

Thread-4写入5完毕

Thread-3写入了4

Thread-1写入了2

Thread-2写入了3

Thread-2写入3完毕

Thread-1写入2完毕

Thread-3写入4完毕

Thread-0写入1完毕

Thread-5读取1

Thread-6读取2

Thread-5读取1完毕

Thread-7读取3

Thread-6读取2完毕

Thread-8读取4

Thread-8读取4完毕

Thread-7读取3完毕

Thread-9读取5

Thread-9读取5完毕


会发现数据比较混乱,没有出现我们想要的情况,一个线程插入的时候被其他线程插队了

然后是加锁后的代码 通过ReentrantReadWriteLock更加细粒度的控制,清楚的指明是读锁还是写锁


package com.wyh.ReadWriteLockTest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
 * @program: JUC
 * @description: 读写锁测试
 * @author: 魏一鹤
 * @createDate: 2022-02-19 21:56
 **/
// 它主要有以下三个情况
//      1. 读取和读取 可以共存的
//      2. 写入和读取 不可以共存的
//      3. 写入和写入 不可以共存的 写的时候一次性只能进入一个线程,只有执行完毕才会释放锁
//ReadWriteLock是一个接口,只有一个实现类:ReentrantReadWriteLock(可重复的读写锁,也叫可重入锁)
//ReadWriteLock维护一对关联的lock 一个用于只读操作,一个用于写入操作,读的时候可以多个线程同时读,写的时候只能被一个线程写
//自定义缓存
public class ReadWriterLockDemo {
public static void main(String[] args){
//创建ReadWriteLock读写锁接口的实现类 可重入读写锁
        ReentrantReadWriteLock readLock = new ReentrantReadWriteLock();
//创建静态资源类
        MyCacheLock myCache = new MyCacheLock();
 //循环创建多线程 这几个线程只做读取的操作
         for (int i = 1; i <= 5; i++) {
  //在lambda表达式中无法直接使用循环变量i 需要定义常量指明变量i做一个中间转换
            //然后操作这个常量
            final  int temp=i;
new Thread(()->{
                myCache.put(temp+"",temp+"");
            }).start();
          }
//循环创建多线程 这几个线程只做存储缓存的操作
             for (int i = 1; i <= 5; i++) {
                 //在lambda表达式中无法直接使用循环变量i 需要定义常量指明变量i做一个中间转换
                 //然后操作这个常量
                 final  int temp=i;
new Thread(()->{
                     myCache.get(temp+"");
                 }).start();
             }
    }
}
//没加锁
class MyCache{
//缓存中一般都是键值对 通过get set 或者put来存或者取
    //volatile保证原子性
    private volatile Map<String,Object> map=new HashMap<>();
//存缓存 实际是一个写的过程
    public void put(String key,Object value){
        System.out.println(Thread.currentThread().getName()+"写入了"+key);
//键值对存缓存
        map.put(key,value);
        System.out.println(Thread.currentThread().getName()+"写入"+key+"完毕");
    }
//取缓存 实际是一个读的过程
    public void get(String key){
        System.out.println(Thread.currentThread().getName()+"读取"+key);
//通过键取值
        Object o = map.get(key);
        System.out.println(Thread.currentThread().getName()+"读取"+key+"完毕");
    }
}
//加锁
//读锁又叫共享锁 一次性只能被一个线程占用 相当于一个原子性操作
//写锁又叫独占锁 多个线程可以同时占有
class MyCacheLock{
//缓存中一般都是键值对 通过get set 或者put来存或者取
    //volatile保证原子性
    private volatile Map<String,Object> map=new HashMap<>();
    //创建  读写锁(可重入锁)
    //它更加细粒度的控制
    //相比于Lock 只能通过lock方法加锁 不能区分读锁还是写锁 但是ReentrantReadWriteLock可以清楚的区分加的是读锁还是写锁
    private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
  //存缓存 实际是一个写的过程 写入的时候 只想同时被一个线程进行写入操作 是一个原子性操作
    public void put(String key,Object value){
        //ReentrantReadWriteLock的用法和Lock差不多 都要通过try catch finally,try中加锁并且写入业务代码 finally中解锁
        try {
 //加一个写锁 并且加锁 = Lock lock=new Lock() lock.lock();
            reentrantReadWriteLock.writeLock().lock();
            System.out.println(Thread.currentThread().getName()+"写入了"+key);
//键值对存缓存
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"写入"+key+"完毕");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
   //解锁 =lock.unLock();
            reentrantReadWriteLock.writeLock().unlock();
        }
    }
    //取缓存 实际是一个读的过程 读取的时候 想同时多个线程进行读取操作  所有线程都可以进行读取操作
    public void get(String key){
        //ReentrantReadWriteLock的用法和Lock差不多 都要通过try catch finally,try中加锁并且写入业务代码 finally中解锁
        try {
            //加一个读锁 并且加锁 = Lock lock=new Lock() lock.lock();
            reentrantReadWriteLock.readLock().lock();
            System.out.println(Thread.currentThread().getName()+"读取"+key);
//通过键取值
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName()+"读取"+key+"完毕");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
//解锁 =lock.unLock();
            reentrantReadWriteLock.readLock().unlock();
        }
    }
}


Thread-0写入了1

Thread-0写入1完毕

Thread-1写入了2

Thread-1写入2完毕

Thread-2写入了3

Thread-2写入3完毕

Thread-3写入了4

Thread-3写入4完毕

Thread-4写入了5

Thread-4写入5完毕

Thread-7读取3

Thread-7读取3完毕

Thread-8读取4

Thread-8读取4完毕

Thread-9读取5

Thread-9读取5完毕

Thread-6读取2

Thread-6读取2完毕

Thread-5读取1

Thread-5读取1完毕


通过使用可重入读写锁发现,结果是我们想要的,读写分明,写的时候是原子性操作,不会被别的线程干涉

总结

使用其他主要是进行创建 然后使用它的读锁加锁/解锁,和写锁加锁/解锁,和Lock用法差不多,也是通过try catch finlly进行的,try中主要指名是什么锁(WriteLock/ReadLock)加锁(Lock)并且写入业务代码,finally中主要进行解锁,由于是手动的,不解锁(unLock)可能会造成死锁,主要代码如下所示


//创建  读写锁(可重入锁)
//它更加细粒度的控制
//相比于Lock 只能通过lock方法加锁 不能区分读锁还是写锁 但是ReentrantReadWriteLock可以清楚的区分加的是读锁还是写锁
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
//加一个写锁 并且加锁 = Lock lock=new Lock() lock.lock();
reentrantReadWriteLock.writeLock().lock(); 
//解锁 =lock.unLock();
reentrantReadWriteLock.writeLock().unlock();
//加一个读锁 并且加锁 = Lock lock=new Lock() lock.lock();
reentrantReadWriteLock.readLock().lock();
//解锁 =lock.unLock();
reentrantReadWriteLock.readLock().unlock();


它主要有以下三个情况

  1. 读取和读取 可以共存的
  2. 写入和读取 不可以共存的
  3. 写入和写入 不可以共存的 写的时候一次性只能进入一个线程,只有执行完毕才会释放锁
目录
相关文章
|
Ubuntu Linux Python
Linux(15)Ubuntu安装ninja构建工具
Linux(15)Ubuntu安装ninja构建工具
2565 0
|
10月前
|
供应链 安全 分布式数据库
探索区块链技术在供应链管理中的应用
【10月更文挑战第21天】 本文深入探讨了区块链技术如何在供应链管理中发挥关键作用,通过具体案例分析,揭示了区块链提高透明度、降低成本和增强安全性的潜力。文章首先概述了区块链技术的基本原理及其对传统供应链模式的挑战,接着详细讨论了区块链如何在不同供应链环节中实施,并分析了其带来的变革。最后,文章提出了企业在采纳区块链技术时可能面临的挑战和应对策略,为供应链管理者提供了宝贵的参考。
497 26
|
12月前
|
JavaScript 编译器 数据安全/隐私保护
TypeScript :关键字
本文介绍了 TypeScript 中的一些核心类型和工具类型,包括 `interface` 和 `type` 的基本使用和区别,以及一些高级类型如 `keyof`、`Record`、`Pick`、`Partial`、`Readonly` 和 `Omit` 的使用方法。文章还详细解释了 `namespace` 的作用和使用场景,帮助开发者更好地组织和管理代码,避免命名冲突,并提高代码的可维护性和可读性。
177 1
|
机器学习/深度学习 人工智能 算法
计算机视觉:目标检测算法综述
【7月更文挑战第13天】目标检测作为计算机视觉领域的重要研究方向,近年来在深度学习技术的推动下取得了显著进展。然而,面对复杂多变的实际应用场景,仍需不断研究和探索更加高效、鲁棒的目标检测算法。随着技术的不断发展和应用场景的不断拓展,相信目标检测算法将在更多领域发挥重要作用。
|
XML 前端开发 API
中台框架的模块开发实践-代码生成器的添加及使用
本文档介绍了如何在中台项目框架 ZhonTai.Core 中集成代码生成器模块,以提升开发效率。首先,需要拉取 ZhonTai.Admin 和 ZhonTai.Module.Dev 的代码仓库,创建模块文件夹并配置后端代码。在后端,通过添加模块类库和路由配置,实现代码生成器服务。接着,配置前端,安装所需依赖,并修改路由配置以添加代码生成器模块。然后,将生成的代码添加到项目中,包括数据库迁移、菜单和权限配置。最后,展示了生成器的使用步骤和效果,包括创建数据表、生成菜单数据以及前端页面展示。文章还提及了后续的扩展计划,如自定义模板管理和通用代码生成器,并提供了相关的代码仓库链接。
80008 5
|
机器学习/深度学习 自然语言处理 算法
【机器学习实战】10分钟学会Python怎么用K均值K-means进行聚类(九)
【机器学习实战】10分钟学会Python怎么用K均值K-means进行聚类(九)
413 0
|
存储 数据采集 大数据
数据仓库面试知识总结
数据仓库面试知识总结
数据仓库面试知识总结
|
人工智能 安全 API
福利来袭——0.1元体验一个月——服务端人脸识别离线SDK!!!
阿里云视觉智能开放平台服务端人脸识别离线SDK上线,0.1元体验一个月。
597 0
福利来袭——0.1元体验一个月——服务端人脸识别离线SDK!!!
|
弹性计算 资源调度 运维
最佳实践丨云上虚拟IDC(私有池)如何为客户业务的确定性、连续性保驾护航
企业业务上云后,还面临特定可用区购买云上特定计算产品实例失败的困境?云上私有池pick一下