读多写少
- 理论上,利用管程和信号量可以解决所有并发问题,但JUC提供了很多工具类,_细分场景优化性能,提升易用性_
- 针对读多写少的并发场景,JUC提供了读写锁,即ReadWriteLock
读写锁
- 读写锁是一种广泛使用的通用技术,并非Java所特有
- 所有读写锁都遵守3条基本原则
- 允许多个线程同时读共享变量 – 与互斥锁的重要区别
- 只允许一个线程写共享变量
- 如果一个写线程正常执行写操作,此时禁止读线程读取共享变量
缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| public class Cache<K, V> { private final Map<K, V> map = new HashMap<>(); private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private final Lock readLock = readWriteLock.readLock(); private final Lock writeLock = readWriteLock.writeLock();
public V get(K key) { V v = null; readLock.lock(); try { v = map.get(key); } finally { readLock.unlock(); } if (v != null) { return v; } writeLock.lock(); try { v = map.get(key); if (v == null) { v = loadFromDb(key); map.put(key, v); } } finally { writeLock.unlock(); } return v; }
private V loadFromDb(K key) { return null; }
public V put(K key, V value) { writeLock.lock(); try { return map.put(key, value); } finally { writeLock.unlock(); } } }
|
锁升级
ReadWriteLock不支持锁升级(读锁升级为写锁),readLock还没有释放,因此无法获取writeLock,这会导致_线程阻塞_
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| readLock.lock(); try { V v = map.get(key); if (v == null) { writeLock.lock(); try { map.put(key, loadFromDb(key)); } finally { writeLock.unlock(); } } } finally { readLock.unlock(); }
|
锁降级
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| readLock.lock(); if (!cacheValid) { readLock.unlock(); writeLock.lock(); try { if (!cacheValid) { cacheValid = true; } readLock.lock(); } finally { writeLock.unlock(); } }
try { } finally { readLock.unlock(); }
|
小结
- 读写锁类似于ReentrantLock(可重入),支持公平模式和非公平模式
- 读锁和写锁都实现了java.util.concurrent.locks.Lock接口
- 但只有写锁支持条件变量,读锁是不支持条件变量的,读锁调用newCondition,会抛出UnsupportedOperationException
参考资料
Java并发编程实战