并发编程系列之ReadWriteLock使用
1、什么是ReadWriteLock?
ReadWriteLock是jdk的juc包中提供的读写锁api,维护一对关联的读锁、写锁,读锁可以被多个读线程共享,写锁排他。
2、为什么需要ReadWriteLock?
之前我们喜欢使用ReentrantLock,重入锁,既然提供了ReentrantLock这个api,jdk官方又推出ReadWriteLock,相对ReentrantLock来说,ReadWriteLock是比较省资源的,ReentrantLock虽然也可以保证线程安全,但是比较耗资源的,比如在所有线程都是读锁的情况,这种情况就是线程安全的,就不需要做线程安全控制,直接保证线程并行执行就行,但是ReentrantLock不能做到,所以ReadWriteLock根据一系列规则保证了线程安全也保证了执行效率。
3、ReadWriteLock的常用API
ReadWriteLock在jdk8的juc包里有两个实现类:
ReentrantReadWriteLock的主要方法:
4、读写锁的获取规则
- 如果一个线程已经占用了读锁,其他线程想要申请读锁,是可以申请成功的
- 如果一个线程已经占用了读锁,其他线程想要申请写锁,是不可以申请成功的
- 如果一个线程已经占用了写锁,其他线程想要申请读锁还是写锁,都是不可以申请成功的
总结起来就是:读读共享、其他都互斥(写写互斥、读写互斥、写读互斥)
5、ReadWriteLock的适用场景
知道了ReadWriteLock的特效之后,我们知道相比于 ReentrantLock 适用于一般场合,ReadWriteLock 适用于读多写少的情况,合理使用可以进一步提高并发效率
6、ReadWriteLock的例子
例子,使用ReentrantReadWriteLock
创建读锁和写锁
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockExample {
private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
private static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
private static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
public static void read() {
readLock.lock();
try {
System.out.println(Thread.currentThread().getName() "得到了读锁");
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() "释放读锁");
readLock.unlock();
}
}
public static void write() {
writeLock.lock();
try {
System.out.println(Thread.currentThread().getName() "得到了写锁");
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() "释放写锁");
writeLock.unlock();
}
}
public static void main(String[] args) {
new Thread(() -> read()).start();
new Thread(() -> read()).start();
new Thread(() -> read()).start();
new Thread(() -> write()).start();
new Thread(() -> write()).start();
new Thread(() -> write()).start();
}
}
控制台打印,从打印结果可以看出,读锁可以同时被多个线程获得的,一个线程获取到读锁之后,另外一个线程又获取来了,而写锁必须等线程释放之后,才可以被其他线程获取,所以读锁共享,写锁排他
代码语言:javascript复制Thread-0得到了读锁
Thread-1得到了读锁
Thread-1释放读锁
Thread-0释放读锁
Thread-3得到了写锁
Thread-3释放写锁
Thread-2得到了读锁
Thread-2释放读锁
Thread-4得到了写锁
Thread-4释放写锁
Thread-5得到了写锁
Thread-5释放写锁
锁降级:指的是写锁降级成为读锁。把持住当前写锁的同时,再获取读锁,随后释放写锁的过程。写锁是线程独占的,读锁是共享的。所以写锁降级为读锁是降级。读锁升级为写锁不能实现。例子来自网上,稍作修改:
代码语言:javascript复制import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockCachedDataExample {
class Data {}
class RWDictionary {
private final Map<String, Data> map = new ConcurrentHashMap<>();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final Lock rLock = readWriteLock.readLock();
private final Lock wLock = readWriteLock.writeLock();
public Data get(String key) {
rLock.lock();
try {
return map.get(key);
}finally {
rLock.unlock();
}
}
public Data put(String key , Data value) {
wLock.lock();
try {
return map.put(key , value);
}finally {
wLock.unlock();
}
}
public String[] allKeys() {
rLock.lock();
try {
return (String[]) map.keySet().toArray();
}
finally {
rLock.unlock();
}
}
public void clear() {
wLock.lock();
try {
map.clear();
}
finally {
wLock.unlock();
}
}
}
class CachedData {
Object data;
volatile boolean cacheValid;
final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {
// Must release read lock before acquiring write lock
rwl.readLock().unlock();
rwl.writeLock().lock();
try {
// Recheck state because another thread might have
// acquired write lock and changed state before we did.
if (!cacheValid) {
data = getData();
cacheValid = true;
}
// 【降级】Downgrade by acquiring read lock before releasing write lock
rwl.readLock().lock();
}finally {
rwl.writeLock().unlock();
}
}
try {
use(data);
} finally {
rwl.readLock().unlock();
}
}
private Object getData() {
return null;
}
void use(Object data){}
}
}
附录参考资料
- http://tutorials.jenkov.com/java-concurrency/read-write-locks.html
- https://www.tutorialspoint.com/java_concurrency/concurrency_readwritelock.htm