读写锁既是一个排他锁也是一个共享锁,读锁是共享锁,写锁是排他锁。读操作可以共存,读操作和写操作互斥,写操作和写操作之间互斥。下面是一个使用例子:
public class Demo {
private Map<String, Object> map = new HashMap<>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private Lock readLock = readWriteLock.readLock();
private Lock writeLock = readWriteLock.writeLock();
public Object get(String key) {
readLock.lock();
try {
return map.get(key);
} finally {
readLock.unlock();
}
}
public void set(String key, Object value) {
writeLock.lock();
try {
map.set(key, value);
} finally {
writeLock.unlock();
}
}
}
2.读写锁原理
读写锁需要保存三个状态,写锁重入的次数,读锁的个数,每个读锁重入的次数。记录写锁和读锁重入的次数是为了实现锁的可重入,记录读锁的个数是为了在没有线程持有读锁时,写线程能进行写操作。
3.锁降级
锁降级是指锁降级为读锁。在写锁没有释放的时候,获取到读锁,再释放写锁。
我们先来看一个会引起问题的加锁方式。
public class Demo {
private Map<String, Object> map = new HashMap<>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private Lock readLock = readWriteLock.readLock();
private Lock writeLock = readWriteLock.writeLock();
private boolean isUpdate;
public void readWrite() {
readLock.lock(); // 保证可以isUpdate可以拿到最新值
if (isUpdate) {
readLock.unlock();
writeLock.lock(); // 若干个写线程阻塞在这一行
map.put("a","b");
writeLock.unlock();// 写线程释放锁
}
Object object = map.get("a");// 读线程读取值
}
}
当写线程释放锁,其他写线程竞争到锁后,读线程此时读取到的可能是脏值。
锁降级方式加锁:
public class Demo2 {
private Map<String, Object> map = new HashMap<>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private Lock readLock = readWriteLock.readLock();
private Lock writeLock = readWriteLock.writeLock();
private boolean isUpdate;
public void readWrite() {
readLock.lock(); // 保证可以isUpdate可以拿到最新值
if (isUpdate) {
readLock.unlock();
writeLock.lock();
map.put("a","b");
readLock.lock();
writeLock.unlock();
}
Object object = map.get("a");readLock.unlock();
}
}
在释放写锁之前获取读锁,此时其他线程在读线程释放读锁之前无法进行写操作,进而保证了线程安全性。