文章目录

  • 9 读写锁
    • 9.1 悲观锁和乐观锁
    • 9.2 表锁|行锁|读锁|写锁
    • 9.3 读写锁概述
    • 9.4 读写锁的演变
    • 9.5 锁降级的必要性

9 读写锁

9.1 悲观锁和乐观锁

悲观锁:顾名思义,它是干什么都很悲观,所以在操作的时候,每次都先上锁,使用时解锁

乐观锁:它很乐观,多线程,并不上锁,但是会发生线程安全问题,通过比较版本号来同步

9.2 表锁|行锁|读锁|写锁

表锁:整个表操作,不会发生死锁

行锁:每个表中的单独一行进行加锁,会发生死锁

读锁:共享锁(可以有多个人读),会发生死锁

写锁:独占锁(只能有一个人写),会发生死锁

比如说,

1线程修改时候,需要等待2线程读之后

2线程修改时候,需要等待1线程读之后

所以,1-2 线程发生死锁

9.3 读写锁概述

读写锁:一个资源可以被多个读线程访问,也可以被一个写线程访问,但不能同时存在读写线程,读写互斥,读读共享

读写锁 ReentrantReadWriteLock
读锁为 ReentrantReadWriteLock.ReadLock,readLock()方法
写锁为 ReentrantReadWriteLock.WriteLock,writeLock()方法

创建读写锁对象 private ReadWriteLock rwLock = new ReentrantReadWriteLock();
写锁 加锁 rwLock.writeLock().lock();,解锁为rwLock.writeLock().unlock();
读锁 加锁 rwLock.readLock().lock();,解锁为rwLock.readLock().unlock();

在不加读写锁的情况下:

class MyCache{// 需要模仿从Map中取对象,所以先穿件一个map对象private volatile Map<String, Object> map = new HashMap<>();// 放数据public void put(String key, Object value) {try {System.out.println(Thread.currentThread().getName()+"正在写操作"+key);// 暂停一会TimeUnit.MICROSECONDS.sleep(300);// 放数据map.put(key, value);System.out.println(Thread.currentThread().getName()+"写完了"+key);} catch (InterruptedException e) {e.printStackTrace();}}// 取数据public void get(String key) {try {System.out.println(Thread.currentThread().getName()+"正在取操作"+key);// 暂停一会TimeUnit.MICROSECONDS.sleep(300);// 放数据map.get(key);System.out.println(Thread.currentThread().getName()+"取完了"+key);} catch (InterruptedException e) {e.printStackTrace();}}
}public class ReadWriteLockTest {public static void main(String[] args) {MyCache myCache = new MyCache();for (int i = 1; i <= 6; i++) {final int num = i;new Thread(()->{myCache.put(String.valueOf(num),String.valueOf(num));},String.valueOf(i)).start();}for (int i = 1; i <= 6; i++) {final int num = i;new Thread(()->{myCache.get(String.valueOf(num));},String.valueOf(i)).start();}}
}

输出结果如下:

1正在写操作1
4正在写操作4
3正在写操作3
2正在写操作2
5正在写操作5
6正在写操作6
1正在取操作1
2正在取操作2
3正在取操作3
4正在取操作4
5正在取操作5
6正在取操作6
2取完了2
2写完了2
5写完了5
5取完了5
6取完了6
3写完了3
4写完了4
1写完了1
6写完了6
4取完了4
3取完了3
1取完了1

很显然,线程在写操作的时候,有线程在读操作,这可能会出现脏数据

加上读写锁:

class MyCache{// 需要模仿从Map中取对象,所以先穿件一个map对象private volatile Map<String, Object> map = new HashMap<>();// 创建读写锁private ReadWriteLock rwlock = new ReentrantReadWriteLock();// 放数据public void put(String key, Object value) {// 添加写锁rwlock.writeLock().lock();try {System.out.println(Thread.currentThread().getName()+"正在写操作"+key);// 暂停一会TimeUnit.MICROSECONDS.sleep(300);// 放数据map.put(key, value);System.out.println(Thread.currentThread().getName()+"写完了"+key);} catch (InterruptedException e) {e.printStackTrace();} finally {// 释放写锁rwlock.writeLock().unlock();}}// 取数据public void get(String key) {// 添加读锁rwlock.readLock().lock();;try {System.out.println(Thread.currentThread().getName()+"正在取操作"+key);// 暂停一会TimeUnit.MICROSECONDS.sleep(300);// 放数据map.get(key);System.out.println(Thread.currentThread().getName()+"取完了"+key);} catch (InterruptedException e) {e.printStackTrace();} finally {// 释放读锁rwlock.readLock().unlock();}}
}

得到的结果为:

2正在写操作2
2写完了2
1正在写操作1
1写完了1
3正在写操作3
3写完了3
4正在写操作4
4写完了4
5正在写操作5
5写完了5
6正在写操作6
6写完了6
1正在取操作1
2正在取操作2
5正在取操作5
3正在取操作3
6正在取操作6
4正在取操作4
1取完了1
3取完了3
2取完了2
4取完了4
6取完了6
5取完了5

9.4 读写锁的演变

无锁:多线程抢夺资源

synchronized和ReentrantLock,都是独占,每次只可以一个操作,不能共享

**ReentrantReadWriteLock,**读读可以共享,提升性能,但是不能多人写。缺点:造成死锁(一直读,不能写),读进程不能写,写进程可以读。

写锁降级为读锁(一般等级写锁高于读锁)

具体第四步演练的代码

获取写锁->获取读锁->释放写锁->释放读锁

//演示读写锁降级
public class Demo1 {public static void main(String[] args) {//可重入读写锁对象ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();//读锁ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();//写锁//锁降级//1 获取写锁writeLock.lock();System.out.println("---write");//2 获取读锁readLock.lock();System.out.println("---read");//3 释放写锁writeLock.unlock();//4 释放读锁readLock.unlock();}
}

倘若先读再写,由于读写锁的特点,在读锁后不允许写,所以会被阻塞

9.5 锁降级的必要性

锁降级中读锁的获取是否必要呢?答案是必要的。

我们在使用读写锁时遵守下面的获取规则

1.如果有一个线程已经占用了读锁,则此时其他线程如果要申请读锁,可以申请成功。

2.如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁,因为读写不能同时操作。

3.如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,都必须等待之前的线程释放写锁,同样也因为读写不能同时,并且两个线程不应该同时写

主要是为了保证数据的可见性,如果当前线程不获取读锁而是直接释放写锁, 假设此刻另一个线程(记作线程T)获取了写锁并修改了数据,那么当前线程无法感知线程T的数据更新。如果当前线程获取读锁,即遵循锁降级的步骤,则线程T将会被阻塞,直到当前线程使用数据并释放读锁之后,线程T才能获取写锁进行数据更新。

这时因为可能存在一个事务线程不希望自己的操作被别的线程中断,而这个事务操作可能分成多部分操作更新不同的数据(或表)甚至非常耗时。如果长时间用写锁独占,显然对于某些高响应的应用是不允许的,所以在完成部分写操作后,退而使用读锁降级,来允许响应其他进程的读操作。只有当全部事务完成后才真正释放锁。

所以总结下锁降级的意义应该就是:在一边读一边写的情况下提高性能。

如果是读之后再写,执行不了 因为读锁权限小于写锁 需要读完之后释放读锁,在进行写锁

【JUC并发编程09】读写锁相关推荐

  1. Java Review - 并发编程_读写锁ReentrantReadWriteLock的原理源码剖析

    文章目录 ReentrantLock VS ReentrantReadWriteLock 类图结构 非公平的读写锁实现 写锁的获取与释放 void lock() void lockInterrupti ...

  2. Java并发编程-ReadWriteLock读写锁

    1.ReadWriteLock介绍 为什么我们有了Lock,还要用ReadWriteLock呢.我们对共享资源加锁之后,所有的线程都将会等待.Lock读操作也锁,写操作也会锁,而对共享资源读的时候,其 ...

  3. Java 并发编程之读写锁 ReentrantReadWriteLock

    ReentrantReadWriteLock 允许多个读操作同时进行,但是只允许一个写操作同时进行,在某些情况下,可以实现更高的并发性. 举个栗子: public class ReadAndWrite ...

  4. 【尚硅谷】大厂必备技术之JUC并发编程——笔记总结

    [JUC并发编程01]JUC概述 关键字:进程和线程.进程和线程.wait和sleep.并发与并行.管程.用户线程和守护线程 [JUC并发编程02]Lock接口 关键字:synchronized.Lo ...

  5. JUC并发编程第十四篇,StampedLock(邮戳锁)为什么比ReentrantReadWriteLock(读写锁)更快!

    JUC并发编程第十四篇,StampedLock(邮戳锁)为什么比ReentrantReadWriteLock(读写锁)更快! 一.ReentrantReadWriteLock(读写锁) 1.读写锁存在 ...

  6. ❤️《JUC并发编程从入门到高级》(建议收藏)❤️

    JUC并发编程 1.什么是JUC JUC的意思就是java并发编程工具包,与JUC相关的有三个包:java.util.concurrent.java.util.concurrent.atomic.ja ...

  7. 多线程进阶=》JUC并发编程02

    在JUC并发编程01中说到了,什么是JUC.线程和进程.Lock锁.生产者和消费者问题.8锁现象.集合类不安全.Callable(简单).常用辅助类.读写锁 https://blog.csdn.net ...

  8. 基于《狂神说Java》JUC并发编程--学习笔记

    前言: 本笔记仅做学习与复习使用,不存在刻意抄袭. -------------------------------------------------------------------------- ...

  9. Java JUC并发编程详解

    Java JUC并发编程详解 1. JUC概述 1.1 JUC简介 1.2 进程与线程 1.2 并发与并行 1.3 用户线程和守护线程 2. Lock接口 2.1 Synchronized 2.2 什 ...

最新文章

  1. RouterOS连载3:架设PPPoE服务
  2. ganglia metric 默认监控项翻译
  3. Intellij IDEA SpringBoot项目热部署解决方案
  4. 逻辑判断 java_写 JS 逻辑判断,不要只知道用 if-else 和 switch
  5. vue用户行为收集_vue 实现移动端键盘搜索事件监听
  6. 语音社交产品,安全合规“防坑指南”!
  7. Centos下 自动化配置SSH免密码登陆
  8. 14 张Python数据科学速查表
  9. date类型_Quartz与Date---cron的相互转换
  10. 太可怕!儿童智能手表竟成偷窥器,315重锤个人隐私泄露
  11. 深度学习在其他领域的应用1:密码破解
  12. 低代码,是否能“取代”开发者?
  13. python需要下载哪些软件-学python下载什么软件开发
  14. Qt 学习之路 2(2):Qt 简介 笔记
  15. 前端基础学习之CSS样式
  16. 转 PHP函数---$_Get()和$_Post()的用法
  17. 转发:Android自动开关机实现
  18. 4571: [Scoi2016]美味
  19. NosqlBooster For MongoDB解决License问题
  20. GOME-2 SIF 数据链接

热门文章

  1. android除去标题栏或全屏
  2. 递归函数练习——累乘
  3. 蓝牙BLE ATT剖析(一)
  4. python常用导入函数及其他操作备忘录
  5. 测验7: 文件和数据格式化 (第7周)
  6. sql 命令重启计算机,如何重启SQL服务
  7. 深划痕需要大面积补漆吗_剐蹭了需要立马补漆吗?这些小技巧能省不少!
  8. (chap7 确保WEB安全的HTTPS) HTTPS加密
  9. 【django】HttpRequest对象
  10. linux kernel中__setup()函数介绍