介绍:
ReentrantLock,可重入锁,是一种递归无阻塞的同步机制。它可以等同于 synchronized 的使用,但是 ReentrantLock 提供了比 synchronized 更强大、灵活的锁机制,可以减少死锁的发生,同时ReentrantLock还可以实现公平锁机制
- 可重入:广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁。ReentrantLock和synchronized都是可重入锁
- 公平锁 /非公平锁:公平锁与非公平锁的区别在于,公平锁的锁获取是有顺序的。但是公平锁的效率没有非公平锁的效率高,在许多线程访问的情况下,公平锁表现出较低的吞吐量
注:ReentrantLock默认非公平锁*
公平锁 new ReentrantLock(true)
非公平锁 new ReentrantLock()
读写锁(ReentrantReadWriteLock):
- 如果当前没有写锁或读锁时,第一个获取锁的线程都会成功,无论该锁是写锁还是读锁。
- 如果当前已经有了读锁,那么这时获取写锁将失败,获取读锁有可能成功也有可能失败
- 如果当前已经有了写锁,那么这时获取读锁或写锁,如果线程相同(可重入),那么成功;否则失败
一把读写锁具备三种状态:
- 读模式下加锁状态 (读锁)
- 写模式下加锁状态 (写锁)
- 不加锁状态
读写锁使用:
public class Demo {
private Map<String, Object> map=new HashMap<>();
private ReadWriteLock rwl=new ReentrantReadWriteLock();
private Lock r=rwl.readLock();
private Lock w=rwl.writeLock();
public Object get(String key){
r.lock();
System.out.println(Thread.currentThread().getName()+"读操作正在执行。。。");
try {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return map.get(key);
} finally{
r.unlock();
System.out.println(Thread.currentThread().getId()+"读操作执行完毕。。。");
}
}
public void put(String key,Object value){
w.lock();
System.out.println(Thread.currentThread().getName()+"写操作在执行。。。");
try {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
map.put(key, value);
} finally {
w.unlock();
System.out.println(Thread.currentThread().getName()+"写操作执行完毕。。。");
}
}
}
锁的降级
如果当先线程是写锁的持有者,并保持获得写锁的状态,同时又获取到读锁,然后释放写锁的过程。(注意不同于这样的分段过程:当前线程拥有写锁,释放掉写锁之后再获取读锁的过程,这种分段过程不能称为锁降级)
class CachedData {
Object data;
volatile boolean cacheValid;
final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
void processCachedData() {
// 获取读锁
rwl.readLock().lock();
if (!cacheValid) {
// 在获取写锁之前必须释放读锁,不释放的话下面写锁会获取不成功,造成死锁
rwl.readLock().unlock();
// 获取写锁
rwl.writeLock().lock();
try {
// 重新检查state,因为在获取写锁之前其他线程可能已经获取写锁并且更改了state
if (!cacheValid) {
data = ...
cacheValid = true;
}
// 通过在释放写锁定之前获取读锁定来降级
// 这里再次获取读锁,如果不获取,那么当写锁释放后可能其他写线程再次获得写锁,导致下方`use(data)`时出现不一致的现象
// 这个操作就是降级
rwl.readLock().lock();
} finally {
rwl.writeLock().unlock(); // 释放写锁,由于在释放之前读锁已经被获取,所以现在是读锁获取状态
}
}
try {
// 使用完后释放读锁
use(data);
} finally {
rwl.readLock().unlock(); //释放读锁
}
}
}}