写在前面:
学习本文前,建议先了解一下 可重入锁 ReentrantLock ,理解一下可重入
是个什么概念
1.概念
既然已经有了 可重入锁 ReentrantLock,为什么还要有 可重入读写锁 ReentrantReadWriteLock
呢??
因为 可重入锁 ReentrantLock
和 synchronized
一样,都是属于独占锁
。所谓 独占,即:同一时间只能有一个线程持有锁
。ReentrantReadWriteLock 的出现, 就是解决 ReentrantLock 独占锁 带来的性能问题。使用 ReentrantLock 无论是 "写/写"线程、"读/读"线程、"读/写"线程之间的工作都是互斥,同时只能有一个线程进入同步区域。
然而,在工作的大多场景下,"读/读"线程之间并不会存在互斥的关系,只有"读/写"线程 或 "写/写"线程间的操作才需要互斥
。因此,可重入读写锁 ReentrantReadWriteLock 就出现了。重点:ReentrantReadWriteLock 目的就是:提高读操作的吞吐量(可用于读多写少的情况下)。
ReentrantReadWriteLock 实现的是 ReadWriteLock 接口,而并不是 Lock接口(ReentrantLock实现的是Lock接口)
。该接口提供了readLock
和writeLock
两种锁的操作机制,一个是读锁,一个是写锁。在读写锁 ReadWriteLock
中,读锁是共享锁,写锁是独占锁(排它锁)。 此处以两个线程为例来介绍:(分为以下三种情况)
- 读/读状态: 两个线程都能获得锁;
- 读/写状态: 只有一个线程能够获得锁;
- 写/写状态: 有一个线程能够获得锁
2.实例
现在有两个线程,分别模拟读/读
、读/写
、写/写
三种情况,来了解两个线程获取锁的情况。此处为了模拟效果,我们会对线程1进行 sleep 休眠操作。(sleep操作会释放当前线程的CPU资源,但是不会释放持有的锁。wait操作才会释放锁,供其他线程使用)
1.(读/读)模式
如下代码所示,设置线程1 无限制休眠,因为使用的是 ReentrantReadWriteLock读写锁
,所以 (读/读)模式 之间不存在互斥关系,此时两个线程都是能够获取到锁的。(本文往下翻会介绍使用 ReentrantLock来实现(读/读)模式,它是存在互斥关系的。而并不是ReadWriteLock 中的读锁是共享锁)
/**
* TODO 读/读模式
*
* @author liuzebiao
* @Date 2019-12-25 18:33
*/
public class RWLockDemo {
static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
static Lock read = readWriteLock.readLock(); //读锁
static Lock write = readWriteLock.writeLock();//写锁
public static void main(String[] args) {
//线程1
new Thread(()->{
try {
read.lock();
System.out.println(Thread.currentThread().getName()+"获得线程");
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
read.unlock();
}
},"线程1").start();
//线程2
new Thread(()->{
try {
read.lock();
System.out.println(Thread.currentThread().getName()+"获得线程");
for (int i = 0; i < 5; i++) {
System.out.println("线程2执行操作");
}
} finally {
read.unlock();
}
},"线程2").start();
}
}
执行结果:
线程1获得线程
线程2获得线程
线程2执行操作
线程2执行操作
线程2执行操作
2.(读/写)模式
如下代码所示,ReentrantReadWriteLock读写锁
中,(读/写)模式 之间是存在互斥关系,此时线程1 处于无限制休眠状态,并不能够释放锁,此时线程2就永远不会获取到锁!!!就更不会执行 System.out 输出操作了
/**
* TODO 读/写模式
*
* @author liuzebiao
* @Date 2019-12-25 18:33
*/
public class RWLockDemo {
static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
static Lock read = readWriteLock.readLock(); //读锁
static Lock write = readWriteLock.writeLock();//写锁
public static void main(String[] args) {
//线程1
new Thread(()->{
try {
write.lock();// 写锁
System.out.println(Thread.currentThread().getName()+"获得线程");
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
write.unlock();
}
},"线程1").start();
//线程2
new Thread(()->{
try {
read.lock();//读锁
System.out.println(Thread.currentThread().getName()+"获得线程");
for (int i = 0; i < 5; i++) {
System.out.println("线程2执行操作");
}
} finally {
read.unlock();
}
},"线程2").start();
}
}
执行结果:
线程1获得线程
3.(写/写)模式
如下代码所示,ReentrantReadWriteLock读写锁
中,(写/写)模式 之间也是存在互斥关系,此时线程1 处于无限制休眠状态,并不能够释放锁,此时线程2也是永远不会获取到锁!!!就更不会执行 System.out 输出操作了
/**
* TODO 写/写模式
*
* @author liuzebiao
* @Date 2019-12-25 18:33
*/
public class RWLockDemo {
static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
static Lock read = readWriteLock.readLock(); //读锁
static Lock write = readWriteLock.writeLock();//写锁
public static void main(String[] args) {
//线程1
new Thread(()->{
try {
write.lock();// 写锁
System.out.println(Thread.currentThread().getName()+"获得线程");
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
write.unlock();
}
},"线程1").start();
//线程2
new Thread(()->{
try {
read.lock();//读锁
System.out.println(Thread.currentThread().getName()+"获得线程");
for (int i = 0; i < 5; i++) {
System.out.println("线程2执行操作");
}
} finally {
read.unlock();
}
},"线程2").start();
}
}
执行结果:
线程1获得线程
3.使用 ReentrantLock 模拟读/读操作
/**
* TODO ReentrantLock 模拟 读/读操作
*
* @author liuzebiao
* @Date 2019-12-25 18:33
*/
public class RWLockDemo {
// static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
static ReentrantLock lock = new ReentrantLock();
// static Lock read = readWriteLock.readLock(); //读锁
// static Lock write = readWriteLock.writeLock();//写锁
public static void main(String[] args) {
//线程1
new Thread(()->{
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+"获得线程");
TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
},"线程1").start();
//线程2
new Thread(()->{
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+"获得线程");
for (int i = 0; i < 5; i++) {
System.out.println("线程2执行操作");
}
} finally {
lock.unlock();
}
},"线程2").start();
}
}
执行结果:
线程1获得线程
4.总结:
使用 ReentrantLock 方式来处理 读/读模式
,通过结果证明它是独占锁。而在上文中使用 ReentrantReadWriteLock 示例中,读锁是共享锁,在处理读/读模式
时,每个线程都是可以获得锁的。这也就印证了开篇一句话:。重点:ReentrantReadWriteLock 目的就是:提高读操作的吞吐量。
可重入读写锁 ReentrantReadWriteLock,介绍到此为止
如果本文对你有所帮助,那就给我点个赞呗
End
转载:https://blog.csdn.net/lzb348110175/article/details/105510930