首页 > 其他分享 >ReentrantReadWriteLock读写锁

ReentrantReadWriteLock读写锁

时间:2023-11-24 22:45:19浏览次数:30  
标签:ReentrantReadWriteLock 写锁 读写 获取 读锁 线程 key

ReentrantReadWriteLock读写锁

乐观锁和悲观锁

乐观锁

乐观锁,就是给需要共享的数据,添加一个版本号version,例如1,每次有线程更新共享数据后,version+1,每次线程进行数据更新时,要比较当前线程持有的数据的版本号,相等则修改,不相等则不修改,支持并发访问。

悲观锁

悲观锁,就是每次只能有一个线程,访问共享的数据,其他线程都阻塞,只有当前线程结束,才会释放锁,其他线程中的一个才能访问,不支持并发访问。

表锁和行锁

表锁

线程涉及到数据库的修改时,其他线程不能修改整个表中的任意行数据,就是表锁,表锁不会出现行锁。

行锁

线程涉及到数据库的修改时,只锁当前的一行,是行锁,可能会出现死锁。

读写锁

读锁是共享锁,写锁是独占锁。都可能出现死锁。
读写锁,一个资源可以被多个读线程访问,或者一个写线程访问,但是不能同时存在读写线程,读写是互斥的,读读是共享的。

案例及代码实现
//1.定义资源类
class MyCache {

    //volatile关键字,共享的数据,在一个线程修改后,被其他线程访问到
    private volatile HashMap<String, Object> hashMap = new HashMap<>();

    private ReadWriteLock rwLock = new ReentrantReadWriteLock();

    //2.定义操作资源类的方法
    public Object get(String key) {
        rwLock.readLock().lock();//读锁
        Object result = null;
        try {
            System.out.println(Thread.currentThread().getName() + "正在读取值" + key);

            TimeUnit.MILLISECONDS.sleep(300);
            result = hashMap.get(key);
            System.out.println(Thread.currentThread().getName() + "读取值完成" + key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            rwLock.readLock().unlock();
        }
        return result;
    }

    public void put(String key, Object value) {
        rwLock.writeLock().lock();//写锁
        try {
            System.out.println(Thread.currentThread().getName() + "正在添加值" + key);
            TimeUnit.MILLISECONDS.sleep(300);
            hashMap.put(key, value);
            System.out.println(Thread.currentThread().getName() + "添加值完成" + key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            rwLock.writeLock().unlock();
        }

    }


}

public class ReadWriteLockDemo {
    public static void main(String[] args) {
        MyCache cache = new MyCache();
        
        //创建线程,向缓存中添加值
        for (int i = 0; i < 5; i++) {
            final int num = i;
            new Thread(() -> {
                cache.put(num + "", num);
            }, String.valueOf(i)).start();
        }

        //创建线程,向缓存中获取值
        for (int i = 0; i < 5; i++) {
            final int num = i;
            new Thread(() -> {
                cache.get(num + "");
            }, String.valueOf(i)).start();
        }

    }
}

读写锁的演变

读写锁的降级

降级案例及代码
/**
 * @author 长名06
 * @version 1.0
 * 读写锁降级演示
 */
public class ReadWriteLockDownLevelDemo {
    public static void main(String[] args) {
        ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
        ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();//写锁
        ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();//读锁
		//先读后写,获取读锁后,就获取不到写锁,线程阻塞

        //1.获取写锁
        writeLock.lock();
        System.out.println("获取写锁");

        //2.获取读锁
        readLock.lock();
        System.out.println("获取读锁");

        //3.释放写锁
        writeLock.unlock();

        //4.释放读锁
        readLock.unlock();
    }
}

小结

  • 1.在线程持有读锁的情况下,该线程不能取得写锁,因为获取写锁时,如果写锁对应的读锁被占用,就马上获取失败,不管读锁是否是当前线程持有(原因,此写锁对应的读锁被线程持有,证明有线程正在读取数据,这是为了避免出现幻读现象)。
  • 2.在线程持有写锁的情况下,该线程可以继续获取读锁。获取读锁时,如果发现写锁被占用,只有写锁没有被当前线程占用的情况下才会获取失败。
  • 原因,读锁是共享锁,即当某个线程获取读锁时,可能有其他线程同时也在持有读锁;而对于已获取写锁的线程,它一定独占了写锁因此可以继续让其获取读锁,当该线程同时获取了写锁和读锁时,还可以先释放写锁继续持有读锁,这样就称为写锁的降级。
    只是为了记录自己的学习历程,且本人水平有限,不对之处,请指正。

标签:ReentrantReadWriteLock,写锁,读写,获取,读锁,线程,key
From: https://www.cnblogs.com/changming06/p/17854905.html

相关文章

  • 3.2 Windows驱动开发:内核CR3切换读写内存
    CR3是一种控制寄存器,它是CPU中的一个专用寄存器,用于存储当前进程的页目录表的物理地址。在x86体系结构中,虚拟地址的翻译过程需要借助页表来完成。页表是由页目录表和页表组成的,页目录表存储了页表的物理地址,而页表存储了实际的物理页框地址。因此,页目录表的物理地址是虚拟地址翻译......
  • Python常见文件读写方法有哪些?
    在Python中,文件读写是非常常见的操作之一,因此提供了多种文件读写模式以及文件读写方法。那么Python常见文件读写方法有哪些?具体请看下文。文件读写模式在Python中,文件读写模式是指打开文件时使用的模式。Python提供了多种文件读写模式,包括:①读模式("r"):以只读方式......
  • file函数读写文件(txt)操作
    文件操作open()方法用于打开一个文件,并返回文件对象,在对文件进行处理过程都需要使用到这个函数格式:open(file_name[,accesss_mode][,buffering])1、file_name:文件路径,必传参数2、accesss_mode:打开文件的模式(只读(r),写入(w),追加(a)等,默认为只读(r))。非必传3、buffering:为0表示不会......
  • STM32使用SDIO模式和DMA实现SD卡的读写及擦除操作
    对于STM32操作SD卡来说,最重要的就算初始化、写操作、读操作、擦除这几个操作了。对于初始化部分上一篇文章已经分析,本篇就主要分析写、读、擦除操作。本篇函数来自于STM32提供的例程。参考野火的程序进行了解释,与野火函数有些不同。这几种函数完成之后,就是开始实现对SD卡进行操作了......
  • MySQL主从搭建及Django实现读写分离
    mysql主从搭建#1主从同步的流程或原理1)master会将变动记录到二进制日志里面;2)master有一个I/O线程将二进制日志发送到slave;3)slave有一个I/O线程把master发送的二进制写入到relay日志里面;4)slave有一个SQL线程,按照relay日志处理slave的数据;#2在home目录下创建mys......
  • 实现数据库的读写分离
    简介轻量级Java框架,在Java的JDBC层提供额外服务,以jar包的形式提供服务(增强版数据库连接驱动)。适用于基于JDBC的ORM框架、支持第三方数据库连接池、支持实现了JDBC规范的数据库。 读写分离:基于已配置好主从复制的多个数据库。 使用步骤在springboot项目中使用。一、......
  • 8.1 Windows驱动开发:内核文件读写系列函数
    在应用层下的文件操作只需要调用微软应用层下的API函数及C库标准函数即可,而如果在内核中读写文件则应用层的API显然是无法被使用的,内核层需要使用内核专有API,某些应用层下的API只需要增加Zw开头即可在内核中使用,例如本章要讲解的文件与目录操作相关函数,多数ARK反内核工具都具有对......
  • 小甲鱼Delphi教程37课《读写修改记录型文件》源码
    说明:本程序用delphi10.4重写编写一、最后效果二、源码1unitUnit1;23interface45uses6Winapi.Windows,Winapi.Messages,System.SysUtils,System.Variants,System.Classes,Vcl.Graphics,7Vcl.Controls,Vcl.Forms,Vcl.Dialogs,Vcl.StdCtrls,V......
  • 基于stm32H730的解决方案开发之SD卡的读写调试
    一概述在嵌入式小系统领域,SD卡存储是一个非常重要的功能。可从难度上,它又是非常难的。因为它涉及到两个大的功能点,一个是文件系统,这个难度非一般。另外一个是sd卡的底层驱动。涉及到的接口多,所以也是一个难度高的地方。两个混合在一起,非常容易出问题。笔者在这块花费了很多时......
  • 35文本文件的读写
    一、最后效果   二、代码:1procedureTForm1.Button1Click(Sender:TObject);2begin3ifOpenDialog1.Executethen4Edit1.Text:=OpenDialog1.FileName;5end;678procedureTForm1.Button2Click(Sender:TObject);9var10SFileName,DFile......