一、ReadWriteLock读写锁
读写锁ReadWriteLock
,是JDK1.5 中提供的读写分离锁,读写分离锁可以有效地帮助减少锁竞争,以提升系统性能。
ReadWriteLock
维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有 writer,读取锁可以由多个 reader 线程同时保持。写入锁是独占的
接口 | 描述 |
---|---|
Lock readLock() |
返回用于读取操作的锁 |
Lock writeLock() |
返回用于写入操作的锁。 |
1.1 案例
案例,三个写线程,三个读线程
/**
*ReentrantReadWriteLock 读写互斥,
*如果读操作被上锁,写操作就不能进行,
*如果写操作被上锁,读操作就不能进行,
*
*读操作上锁后,需要解锁后, 写才能上锁。
* 如果读没有解锁,还调用了写的锁,就会造成堵塞,让线程卡在哪里。
* 反之却是可以的,即在写没有解锁,读操作上锁是可以的。(叫做降级锁)
*
*/
public class ReadWriteLockTest {
public static void main(String[] args) {
final Queue q3 = new Queue();
// 弄3个读的线程, 弄3个写的线程
for (int i = 1; i <= 3; i++) {
new Thread() {
public void run() {
while (true) {
q3.put(new Random().nextInt(10000));
}
}
}.start();
new Thread() {
public void run() {
while (true) {
q3.get();
}
}
}.start();
}
}
}
class Queue {
private Object data = null;
// hibernate load的方法实现就是 ReentrantReadWriteLock 放在代理对象中
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
public void get() {
rwl.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "开始取");
Thread.sleep((long) (Math.random() * 1000));
System.out.println(Thread.currentThread().getName() + "取完毕" + data);
} catch (InterruptedException e) {
e.printStackTrace();
}
rwl.readLock().unlock();
}
public void put(Object obj) {
rwl.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "开始写");
Thread.sleep((long) (Math.random() * 1000));
this.data = obj;
System.out.println(Thread.currentThread().getName() + "写结束" + obj);
} catch (InterruptedException e) {
e.printStackTrace();
}
rwl.writeLock().unlock();
}
}
1.2 读写锁实现缓存
下面的代码展示了如何利用重入来执行升级缓存后的锁降级(伪代码)
class CachedData {
Object data;
volatile boolean cacheValid;
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();
// Recheck state because another thread might have acquired
// write lock and changed state before we did.
if (!cacheValid) {
data = ...
cacheValid = true;
}
// Downgrade by acquiring read lock before releasing write lock
//锁降级
rwl.readLock().lock();
rwl.writeLock().unlock(); // Unlock write, still hold read
}
use(data);
rwl.readLock().unlock();
}
}
在使用某些种类的
Collection
时,可以使用ReentrantReadWriteLock
来提高并发性。通常,在预期collection
很大,读取者线程访问它的次数多于写入者线程,并且 entail 操作的开销高于同步开销时,这很值得一试。例如,以下是一个使用TreeMap
的类,预期它很大,并且能被同时访问。
class RWDictionary {
private final Map<String, Data> m = new TreeMap<String, Data>();
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();
public Data get(String key) {
r.lock();
try { return m.get(key); }
finally { r.unlock(); }
}
public String[] allKeys() {
r.lock();
try { return m.keySet().toArray(); }
finally { r.unlock(); }
}
public Data put(String key, Data value) {
w.lock();
try { return m.put(key, value); }
finally { w.unlock(); }
}
public void clear() {
w.lock();
try { m.clear(); }
finally { w.unlock(); }
}
}
模拟缓存
public class Cache {
private Map<String, Object> cache = new HashMap<String, Object>();
private ReadWriteLock rwl = new ReentrantReadWriteLock();
public Object getData(String key){
rwl.readLock().lock();
Object value = null;
try{
value = cache.get(key);
if(value == null){
rwl.readLock().unlock();
rwl.writeLock().lock();
try{
if(value==null){
value = "aaaa";//实际失去queryDB();
}
}finally{
rwl.writeLock().unlock();
}
rwl.readLock().lock();
}
}finally{
rwl.readLock().unlock();
}
return value;
}
}
参考
尚硅谷多线程视频