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 ...
随机推荐
- 无监督LDA、PCA、k-means三种方法之间的的联系及推导
\(LDA\)是一种比较常见的有监督分类方法,常用于降维和分类任务中:而\(PCA\)是一种无监督降维技术:\(k\)-means则是一种在聚类任务中应用非常广泛的数据预处理方法. 本文的 ...
- ping外网:unknown host www.baidu.comc排查
ping 百度出现:(ping www.baidu.com) "ping: unknown host www.baidu.com" 1.ping 网关确定是否连接上路由器,并 ...
- JVM内存结构详解
从java编程语言说起... 1. Java编程语言简介 1.1 编程语言概述 系统级和应用级 系统级:C,C++,go,erlang 应用级:C#,Java,Python,Perl,Ruby,php ...
- Second Large Rectangle【单调栈】
Second Large Rectangle 题目链接(点击) 题目描述 Given a N×MN \times MN×M binary matrix. Please output the size ...
- 从零开始的Spring Boot(6、Thymeleaf内置对象及表达式大全)
1.1 基础对象 #ctx:上下文对象 ${#ctx.locale} ${#ctx.variableNames} ${#ctx.request} ${#ctx.response} ${#ctx.ses ...
- SpringBoot 构建 Docker 镜像的 3 种方式
本文将介绍3种技术,通过 Maven 把 SpringBoot 应用构建成 Docker 镜像. (1)使用 spring-boot-maven-plugin 内置的 build-image. (2) ...
- cc28c_demo.cpp,派生类的构造函数和析构函数-代码示范3
cc28c_demo.cpp,派生类的构造函数和析构函数-代码示范3 //派生类的构造函数和析构函数//派生类的构造函数(执行步骤)//--执行基类的构造函数//--执行成员对象的构造函数//--执行 ...
- 腾讯云Redis混合存储版重磅推出,万字长文助你破解缓存难题!
导语 | 缓存+存储的系统架构是目前常见的系统架构,缓存层负责加速访问,存储层负责存储数据.这样的架构需要业务层或者是中间件去实现缓存和存储的双写.冷热数据的交换,同时还面临着缓存失效.缓存刷脏.数据 ...
- 9、ssh的集成方式2
1.在第一种的集成方式中,通过struts2-spring-plugin-2.1.8.1.jar这个插件让spring自动产生对应需要的action类,不需要在对应的spring.xml文件中进行配置 ...
- java 虚拟机指令重新排序
指令重排序是JVM为了优化指令,提高程序运行效率,在不影响单线程程序执行结果的前提下,尽可能地提高并行度.编译器.处理器也遵循这样一个目标.注意是单线程.多线程的情况下指令重排序就会给程序员带来问题. ...