ReadWriteLock锁的应用
对于 Lock 锁来说,如果要实现 “一写多读” 的并发状态(即允许同时读,不允许同时写),需要对 “写操作” 加锁,对 “读操作” 不作要求即可。但是如果对于 “读” 操作下,有 “写操作” 接入的话,对于当前的 “读操作” 可能会产生 “幻读” 的现象。所以对于要实现 “一写多读” 的情况下,应推荐使用 ReadWriteLock 锁。
ReadWriteLock 是与 Lock 平级的一个 JUC 包下的接口
它唯一的实现类是 ReentrantReadWriteLock 类,一个 ReentrantReadWriteLock 对象维护了一对关联的locks ,一个用于只读操作,一个用于写入。
简单来说:
- 它维护了两个锁,一个叫 “读锁”,一个叫 “写锁”。
- 为了允许 “一写多读” 的操作,按理上,“写锁” 一旦上锁,不能再被获取;而为了保证能同时读数据,“读锁” 若上锁,想获取 “读锁” 的线程仍然可以执行读操作,而为了防止 “读操作” 执行时有 “写操作” 的接入,应该要防止 “写锁” 被获取。
下面通过 4 个不同顺序的读写实例来演示一遍(代码贴在最后面)
1、一个线程已获取 “读锁” 状态下,另一个线程尝试获取 “写锁” 。


在 “读锁” 被获取的情况下,“写锁” 不能被获取。(即便是在多个线程都获取 “读锁”,“写锁” 必须在所有 “读操作” 结束后才能被获取,可自行测试)
2、一个线程已获取 “写锁” 状态下,另一个线程尝试获取 “读锁” 。


在 “写锁” 被获取的情况下,“读锁” 不能被获取。
3、一个线程已获取 “读锁” 状态下,另一个线程尝试获取 “读锁” 。


在 “读锁” 被获取的情况下,“读锁” 还能被获取,类似于 Semaphore 辅助类。
4、一个线程已获取 “写锁” 状态下,另一个线程尝试获取 “写锁” 。


在 “写锁” 被获取的情况下,“写锁” 不能被获取。
总代码
package ReadWriteLock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockDemo {
private final static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public static void main(String[] args) throws InterruptedException {
writeAndWriteTest();
}
public static void readAndWriteTst() throws InterruptedException {
//第一个线程先获取写锁,另一个线程再获取读锁
new Thread(() -> {
readWriteLock.writeLock().lock();
System.out.println("写锁已获取");
try {
Thread.sleep(8000);
readWriteLock.writeLock().unlock();
System.out.println("已关闭写锁");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
Thread.sleep(3000);
System.out.println("另一个线程尝试获取读锁");
new Thread(() -> {
readWriteLock.readLock().lock();
System.out.println("另一个线程的读锁已获取");
}).start();
}
public static void writeAndReadTest() throws InterruptedException {
//第一个线程先获取读锁,另一个线程再获取写锁
new Thread(() -> {
readWriteLock.readLock().lock();
System.out.println("读锁已获取");
try {
Thread.sleep(8000);
readWriteLock.readLock().unlock();
System.out.println("已关闭读锁");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
Thread.sleep(3000);
System.out.println("另一个线程尝试获取写锁");
new Thread(() -> {
readWriteLock.writeLock().lock();
System.out.println("另一个线程的写锁已获取");
}).start();
}
public static void readAndReadTest() throws InterruptedException {
//一个线程已获读锁,另一个线程再获取读锁
new Thread(() -> {
readWriteLock.readLock().lock();
System.out.println("读锁已获取");
try {
Thread.sleep(8000);
readWriteLock.readLock().unlock();
System.out.println("已关闭读锁");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
Thread.sleep(3000);
System.out.println("另一个线程尝试获取读锁");
new Thread(() -> {
readWriteLock.readLock().lock();
System.out.println("另一个线程的读锁已获取");
}).start();
}
public static void writeAndWriteTest() throws InterruptedException {
//一个线程已获写锁,另一个线程再获取写锁
new Thread(() -> {
readWriteLock.writeLock().lock();
System.out.println("写锁已获取");
try {
Thread.sleep(8000);
readWriteLock.writeLock().unlock();
System.out.println("已关闭写锁");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
Thread.sleep(3000);
System.out.println("另一个线程尝试获取写锁");
new Thread(() -> {
readWriteLock.writeLock().lock();
System.out.println("另一个线程的写锁已获取");
}).start();
}
}
> 总结
ReentrantReadWriteLock 中的写锁类似于 Lock 锁,而读锁类似于 Semaphore 信号量机制。“写锁” 保证临界资源能被唯一占用,解决了 “写写同步问题”。而当且仅当 “读锁” 队列为空时,“写锁” 才能被获取。且 “写锁” 和 “读锁” 在同一时刻不能同时被持有,解决了 “读写同步问题”,且它还能保证能够 “同时读” 的特性。
ReadWriteLock锁的应用的更多相关文章
- Java并发编程-各种锁
安全性和活跃度通常相互牵制.我们使用锁来保证线程安全,但是滥用锁可能引起锁顺序死锁.类似地,我们使用线程池和信号量来约束资源的使用, 但是缺不能知晓哪些管辖范围内的活动可能形成的资源死锁.Java应用 ...
- java 锁 Lock接口详解
一:java.util.concurrent.locks包下常用的类与接口(lock是jdk 1.5后新增的) (1)Lock和ReadWriteLock是两大锁的根接口,Lock代表实现类是Reen ...
- 【转】java并发编程系列之ReadWriteLock读写锁的使用
前面我们讲解了Lock的使用,下面我们来讲解一下ReadWriteLock锁的使用,顾明思义,读写锁在读的时候,上读锁,在写的时候,上写锁,这样就很巧妙的解决synchronized的一个性能问题:读 ...
- java并发编程实战:第十三章----显示锁
一.Lock与ReentrantLock Lock接口中定义了一种无条件.可轮询的.定时的以及可中断的锁获取操作,所有加锁和解锁的方法都是显式的. 1 public interfece Lock 2 ...
- cocurrent包 锁 Lock
20. 锁 Lock java.util.concurrent.locks.Lock 是一个类似于 synchronized 块的线程同步机制.但是 Lock 比 synchronized 块更加灵活 ...
- java并发编程(七)----(JUC)ReadWriteLock
前面我们已经分析过JUC包里面的Lock锁,ReentrantLock锁和semaphore信号量机制.Lock锁实现了比synchronized更灵活的锁机制,Reentrantlock是Lock的 ...
- StampedLock:一个并发编程中非常重要的票据锁
摘要:一起来聊聊这个在高并发环境下比ReadWriteLock更快的锁--StampedLock. 本文分享自华为云社区<[高并发]一文彻底理解并发编程中非常重要的票据锁--StampedLoc ...
- Java 并发工具包 java.util.concurrent 用户指南
1. java.util.concurrent - Java 并发工具包 Java 5 添加了一个新的包到 Java 平台,java.util.concurrent 包.这个包包含有一系列能够让 Ja ...
- Java并发编程-并发工具包(java.util.concurrent)使用指南(全)
1. java.util.concurrent - Java 并发工具包 Java 5 添加了一个新的包到 Java 平台,java.util.concurrent 包.这个包包含有一系列能够让 Ja ...
随机推荐
- php 加反斜杠的原因与处理办法
php程序加反斜杠的原因就是要进行特殊字符的转义. 默认PHP 指令 magic_quotes_gpc是on的,这时候就可以用stripslashes() 函数删除自动添加的反斜杠. 用法就是:str ...
- 局域网访问电脑中VMware虚拟机
场景 你在自己的台式机或笔记本中使用VMware Workstation搭建了一个虚拟机系统,如Debian.Fedora等Linux系统.现在你希望使用局域网中另一台电脑访问你电脑上的虚拟机系统,怎 ...
- Docker学习 ,超全文档!
我们的口号是:再小的帆也能远航,人生不设限!! 一.学习规划: Docker概述 Docker安装 Docker命令 Docker镜像 镜像命令 容器命令 操作命令 容器数据卷 Doc ...
- 检查*.ldf为何这么大
testdb,只是个测试用文件,备份时突然发现*.ldf怎么这么大,当硬盘不要花银子买啊......--可随意删除...,有空再检查,累了休息... 如批量生成数据.或导入那个来自MySQL的Empl ...
- spark源码解析大全
第1章 Spark 整体概述 1.1 整体概念 Apache Spark 是一个开源的通用集群计算系统,它提供了 High-level 编程 API,支持 Scala.Java 和 Pytho ...
- C++值元编程
--永远不要在OJ上使用值元编程,过于简单的没有优势,能有优势的编译错误. 背景 2019年10月,我在学习算法.有一道作业题,输入规模很小,可以用打表法解决.具体方案有以下三种: 运行时预处理,生成 ...
- 网页中为什么常用AT替换@(repost from https://zhidao.baidu.com/question/122291.html)
经常在个人主页上看到别人的邮箱地址中@被AT符号替代,很是迷惑,这样替代有什么好处呢?还是说html原有的原因使界面中不能出现@,查阅资料后解答如下: 写成AT [at],是为了防止被一些邮件扫描器搜 ...
- 3.WebPack配置文件
一.为什么需要WebPack配置文件 引用自官方: 在 webpack 4 中,可以无须任何配置使用,然而大多数项目会需要很复杂的设置,这就是为什么 webpack 仍然要支持 配置文件.这比在终端( ...
- Java多线程之内存模型
目录 多线程需要解决的问题 线程之间的通信 线程之间的通信 Java内存模型 内存间的交互操作 指令屏障 happens-before规则 指令重排序 从源程序到字节指令的重排序 as-if-seri ...
- 斐讯N1搭建高性能博客
前些日子我在网上淘到了一台斐讯n1,Amlogic S905D的板子让这个盒子平滑地用上了Armbian系统,有了linux的加持,让这个设备的玩法又上升了一个层次,网上大多都是把他作为旁路由用来富强 ...