从有点基础开始学JUC:读写锁
读写锁
概述
事实上,我们之前学习的锁其实都有一点小小的问题,那就是其锁的粒度太大了,像是Synchronized 和 ReentrantLock 都是独占锁,即在同一时刻只有一个线程获取到锁。而许多情况下,我们的许多操作并不是互斥的,完全可以支持并行执行,并不需要加锁来进行限制,十分的不灵活,那么有没有一个能够让我们更加自由的选择加锁的办法呢?
ReentrantReadWriteLock
读写锁,顾名思义是一把锁分为两部分:读锁
和写锁
,其中读锁允许多个线程同时获得,是一个共享锁,因为读操作本身是线程安全的;而写锁则是排他锁,不允许多个线程同时获得写锁,并且写操作和读操作也是互斥的。
主要解决:对共享资源有读和写的操作,且写操作没有读操作那么频繁的场景。
特点
- 公平性:读写锁支持非公平和公平的锁获取方式,非公平锁的吞吐量优于公平锁的吞吐量,默认构造的是非公平锁
- 可重入:在线程获取读锁之后能够再次获取读锁,但是不能获取写锁,而线程在获取写锁之后能够再次获取写锁,同时也能获取读锁
- 锁降级:线程获取写锁之后获取读锁,再释放写锁,这样实现了写锁变为读锁,也叫锁降级
其更适合读多写少的场景。读锁可以被多个线程同时持有, 所以并发能力很高;而写锁是独占锁, 在一个线程持有写锁时候, 其他线程都不能在抢占, 包含抢占读锁都会阻塞
使用
常用方法
方法 | 描述 |
---|---|
public ReentrantReadWriteLock() | 默认构造方法,非公平锁 |
public ReentrantReadWriteLock(boolean fair) | true 为公平锁 |
public ReentrantReadWriteLock.ReadLock readLock() | 返回读锁 |
public ReentrantReadWriteLock.WriteLock writeLock() | 返回写锁 |
public void lock() | 加锁 |
public void unlock() | 解锁 |
public boolean tryLock() | 尝试获取锁 |
现在有个餐厅,其生意非常好,客人特别多,于是其要求来的客人必须先抢票才能进来用餐。于是有很多人想来用餐的就要去抢票,同时还有许多吃瓜群众看还有多少票剩余。我们来实现这个案例
public class Restaurant {
private volatile int num = 6;
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
public void grabTickets() {
boolean isGet = false;
//获取写锁
rwLock.writeLock().lock();
//锁降级,获取读锁
rwLock.readLock().lock();
try {
if (num > 0) {
--num;
isGet = true;
} else {
System.out.println(Thread.currentThread().getName() + "没票了");
}
} finally {
//释放写锁
rwLock.writeLock().unlock();
}
try {
if (isGet) {
System.out.println(Thread.currentThread().getName() + "抢到了票,现在还剩下的票数为:" + num);
}
} finally {
//释放读锁
rwLock.readLock().unlock();
}
}
public void getNum() {
rwLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "查看了现在的票数:" + num);
} finally {
rwLock.readLock().unlock();
}
}
}
我们这里在抢票时,为了其他线程可以更快地查看当前票数,我们采用锁降级。
然后是我们执行的代码
public class RestaurantTest {
public static void main(String[] args) {
Restaurant restaurant = new Restaurant();
for (int i = 0; i < 10; ++i) {
new Thread(restaurant::grabTickets, "食客" + (i + 1)).start();
new Thread(restaurant::getNum, "吃瓜群众" + (i + 1)).start();
}
}
}
来看一下执行结果
用户1抢到了票,现在还剩下的票数为:5
吃瓜群众1查看了现在的票数:5
吃瓜群众2查看了现在的票数:5
用户2抢到了票,现在还剩下的票数为:4
用户3抢到了票,现在还剩下的票数为:3
吃瓜群众3查看了现在的票数:3
用户4抢到了票,现在还剩下的票数为:2
吃瓜群众6查看了现在的票数:2
吃瓜群众4查看了现在的票数:2
吃瓜群众5查看了现在的票数:2
用户7抢到了票,现在还剩下的票数为:1
用户6抢到了票,现在还剩下的票数为:0
用户5没票了
吃瓜群众9查看了现在的票数:0
吃瓜群众7查看了现在的票数:0
用户9没票了
用户8没票了
吃瓜群众8查看了现在的票数:0
用户10没票了
吃瓜群众10查看了现在的票数:0
进程已结束,退出代码0
这就是这篇文章的内容了,欢迎大家的讨论,如有错漏,也请指出,谢谢~
转载自:https://juejin.cn/post/7244466524559900733