可重入读写锁 ReentrantReadWriteLock
1. 前言
从本节开始,我们学习新一章内容 —— 并发锁。
在 “Java 并发原理教程” 中,介绍了锁相关概念和原理知识,本章各小节内容不再过多解释概念,重点为大家介绍具体锁工具的 API 和使用方法。
本节带领大家认识第一个常用的 Java 并发锁工具之 ReentrantReadWriteLock。
本节先简单介绍 ReentrantReadWriteLock 的基本概念,然后介绍关键的编程方法,最后通过一个编程例子为大家展示 ReentrantReadWriteLock 工具类的用法。
下面我们正式开始介绍吧。
2. 概念解释
ReadWriteLock 是一个接口类型,翻译为 “读写锁”。多个线程同时读某个资源时,为了满足并发量,不应该加锁处理,但是如果有一个线程写这个资源,就不应该再有其它线程对该资源进行读操作或者写操作。
实现了此接口的类提供了 “读读能共存、读写不能共存、写写不能共存” 的控制逻辑。当同一个资源被大量线程读取,但仅有少数线程修改时,使用 ReadWriteLock 可大大提高并发效率。比如对一个电商网站的商品,回复评论(写)是不频繁的,但是浏览(读)是非常频繁的,这种情况使用 ReadWriteLock 工具类做并发控制非常适合。总之适合读多写少的场景。
ReentrantLock 翻译为 “可重入锁”。“可重入” 是什么意思呢?就是指一个线程可以多次获取该锁,Java 中 在语言语法层提供的 syncrinized 是最常见的用于并发控制的关键字,其构成的锁也是可重入的。可重入锁在一定程度可以避免死锁的发生。
更多关于锁的原理,可阅读 “Java 并发原理教程”。
ReentrantReadWriteLock 类实现了 “读写锁” 和 “可重入锁” 的双重功能。其本身不提供加锁服务,只负责提供读锁和写锁。在介绍关键编程方法之前,我们先看一张图整体了解关键方法的使用方式。
下面我们学习其关键的编程方法。
3.ReentrantReadWriteLock 的编程方法
- 构造方法 ReentrantReadWriteLock () 和 ReentrantReadWriteLock (boolean fair)
有两个构造方法,在构造对象时,可选择是否构造为公平锁模式。什么是公平锁呢?公平锁指的是获取所的线程按照申请锁的线程获取,而非乱序随机获取到锁权限。 - writeLock () 和 readLock ()
这两个方法分别用于获取写锁和获取读锁,对读操作的逻辑使用读锁对象加锁,对写操作的逻辑使用写锁对象加锁,如此可以做到 “读读能共存、读写不能共存、写写不能共存”,在实现线程安全的情况下,提高并发效率。 - isFair()
获取锁是否具有公平性,此方法返回 boolean 值。 - getWriteHoldCount () 、 getReadHoldCount () 和 getReadLockCount ()
getWriteHoldCount () 方法用于获取当前线程的写锁计数,getReadHoldCount () 方法用于获取当前线程的读锁计数,getReadLockCount () 方法用于获取总的读锁计数。所有方法均返回 int 值。
4.ReentrantReadWriteLock.ReadLock 的编程方法
此类是一个静态内部类,封装在 ReentrantReadWriteLock 中,此类不对外提供构造方法,由 ReentrantReadWriteLock.readLock () 方法获取此类对象。
- lock () 和 lockInterruptibly ()
两个方法都用于加读锁,第二个方法可被中断。 - tryLock () 和 tryLock (long timeout, TimeUnit unit)
两个方法都返回 boolean 值,用于尝试加读锁,第一个方法在尝试不成功时立刻返回,第二个方法可在指定时间内尝试加读锁。这两个方法提供了加锁的柔性,提供了更多操作空间。 - unlock()
释放读锁。
5.ReentrantReadWriteLock.WriteLock 的编程方法
此类是一个静态内部类,封装在 ReentrantReadWriteLock 中,此类不对外提供构造方法,由 ReentrantReadWriteLock.writeLock () 方法获取此类对象。
- lock () 和 lockInterruptibly ()
两个方法都用于加写锁,第二个方法可被中断。 - tryLock () 和 tryLock (long timeout, TimeUnit unit)
两个方法都返回 boolean 值,用于尝试加写锁,第一个方法在尝试不成功时立刻返回,第二个方法可在指定时间内尝试加写锁。这两个方法提供了加锁的柔性,提供了更多操作空间。 - unlock()
释放写锁。
6. 编程案例
上面介绍了核心编程方法,我们举一个编程案例,实际体会一下 ReentrantReadWriteLock 的用法。
import lombok.SneakyThrows;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockTest {
// 创建读写锁对象
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
// 获取读锁对象
private final Lock readlock = readWriteLock.readLock();
// 获取写锁对象
private final Lock writelock = readWriteLock.writeLock();
// 待控制的资源
private int account = 0;
private static ReadWriteLockTest readWriteLockTest = new ReadWriteLockTest();
public static void main(String[] args) {
new Thread(new Runnable() {
@SneakyThrows
public void run() {
while(true) {
Thread.sleep(1000);
int tmp = readWriteLockTest.get();
System.out.println("读操作:" + tmp);
}
}
}).start();
new Thread(new Runnable() {
@SneakyThrows
public void run() {
while(true) {
Thread.sleep(2000);
readWriteLockTest.add(10);
}
}
}).start();
}
public void add(int value) {
// 加写锁
writelock.lock();
try {
account += 1;
} finally {
// 释放写锁
writelock.unlock();
}
}
public int get() {
// 加读锁
readlock.lock();
try {
return account;
} finally {
// 释放读锁
readlock.unlock();
}
}
}
运行上面代码一段时间后结果如下:
读操作:0
读操作:1
读操作:1
读操作:2
读操作:2
注意在使用时,获取锁的操作 lock () 应该放在 try 之前,而释放锁的操作 unlock () 需要放在 finally 中,可确保锁释放。
7. 小结
本节解释了 ReentrantReadWriteLock 的基本概念和应用场合,且通过一个简单的例子展示了其用法,更多关于此工具类的概念和原理介绍,可阅读 “Java 并发原理教程” 。希望大家在学习过程中,多思考勤练习,早日掌握之。
最新评论
1.4 可以嘛?
可以
2022.06.06版本2022.1 临时激活码激活提示key is invalid 热心大佬提供的激活码里跟文章里的激活码是一样的 坐等lz掘地三尺
我打开绿色版,用配色方案的时候,还是弹出付费
非常好用,谢谢!lz好人一生平安!
感谢楼主
东西不错,希望能够继续保持
这个win版破解之后,就不要激活吧~