对于 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锁的应用的更多相关文章

  1. Java并发编程-各种锁

    安全性和活跃度通常相互牵制.我们使用锁来保证线程安全,但是滥用锁可能引起锁顺序死锁.类似地,我们使用线程池和信号量来约束资源的使用, 但是缺不能知晓哪些管辖范围内的活动可能形成的资源死锁.Java应用 ...

  2. java 锁 Lock接口详解

    一:java.util.concurrent.locks包下常用的类与接口(lock是jdk 1.5后新增的) (1)Lock和ReadWriteLock是两大锁的根接口,Lock代表实现类是Reen ...

  3. 【转】java并发编程系列之ReadWriteLock读写锁的使用

    前面我们讲解了Lock的使用,下面我们来讲解一下ReadWriteLock锁的使用,顾明思义,读写锁在读的时候,上读锁,在写的时候,上写锁,这样就很巧妙的解决synchronized的一个性能问题:读 ...

  4. java并发编程实战:第十三章----显示锁

    一.Lock与ReentrantLock Lock接口中定义了一种无条件.可轮询的.定时的以及可中断的锁获取操作,所有加锁和解锁的方法都是显式的. 1 public interfece Lock 2 ...

  5. cocurrent包 锁 Lock

    20. 锁 Lock java.util.concurrent.locks.Lock 是一个类似于 synchronized 块的线程同步机制.但是 Lock 比 synchronized 块更加灵活 ...

  6. java并发编程(七)----(JUC)ReadWriteLock

    前面我们已经分析过JUC包里面的Lock锁,ReentrantLock锁和semaphore信号量机制.Lock锁实现了比synchronized更灵活的锁机制,Reentrantlock是Lock的 ...

  7. StampedLock:一个并发编程中非常重要的票据锁

    摘要:一起来聊聊这个在高并发环境下比ReadWriteLock更快的锁--StampedLock. 本文分享自华为云社区<[高并发]一文彻底理解并发编程中非常重要的票据锁--StampedLoc ...

  8. Java 并发工具包 java.util.concurrent 用户指南

    1. java.util.concurrent - Java 并发工具包 Java 5 添加了一个新的包到 Java 平台,java.util.concurrent 包.这个包包含有一系列能够让 Ja ...

  9. Java并发编程-并发工具包(java.util.concurrent)使用指南(全)

    1. java.util.concurrent - Java 并发工具包 Java 5 添加了一个新的包到 Java 平台,java.util.concurrent 包.这个包包含有一系列能够让 Ja ...

随机推荐

  1. [computer graphics]简单光照模型(Phong和Blinn-Phong)和明暗处理

    简单光照模型(Phong和Blinn-Phong)和明暗处理 支持点光源和平行光,是一种简单光照模型,它将光照分解成了三个部分,分别为 漫反射 镜面反射 环境光 如图所示,是一个简单的几何模型. \( ...

  2. 521.最长特殊序列 I

    2020-05-14 最长特殊序列 I 给你两个字符串,请你从这两个字符串中找出最长的特殊序列. 「最长特殊序列」定义如下:该序列为某字符串独有的最长子序列(即不能是其他字符串的子序列). 子序列 可 ...

  3. Water Testing【皮克定理,多边形面积,线段上点的数目】

    Water Testing 传送门:链接  来源:UPC 9656 题目描述 You just bought a large piece of agricultural land, but you n ...

  4. Fibonacci(模板)【矩阵快速幂】

    Fibonacci 题目链接(点击) Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 20989   Accepted: 14 ...

  5. 8种排序算法 Java实现

    冒泡排序 O(n2) 两个数比较大小,较大的数下沉,较小的数冒起来. public static void bubbleSort(int[] a) { //临时变量 int temp; //i是循环次 ...

  6. FFT快速傅里叶变换的python实现

    FFT是DFT的高效算法,能够将时域信号转化到频域上,下面记录下一段用python实现的FFT代码. # encoding=utf-8 import numpy as np import pylab ...

  7. cb35a_c++_STL_算法_for_each

    cb35a_c++_STL_算法_for_each for_each(b,e,p)使用for_each()算法遍历数据使用for_each()和函数对象修改数据使用for_each()的返回值 //转 ...

  8. 面试题64:求 1 + 2 + ... + n

    这道题目条件限制严格,需要发散思维...但是作者是以 C++ 语言特性来做讲解的,对于 Java 狗只能说稍微有点参考意义吧!

  9. 单元测试中使用mock最好不要使用easymock而应该使用powermock

    视频参考汪文君powermock视频教程相当的经典

  10. SSH网上商城一

    Java高级项目之SSH网上商城项目实战: 1.采用目前最主流的三大框架开发即Struts2+Spring+Hibernate框架整合开发.2.通过AJAX技术提供良好的用户体验.3.提供了邮箱激活的 ...