读写锁接口:ReadWriteLock,它的具体实现类为:ReentrantReadWriteLock
使用场景:
对于一个资源,读读能共存,读写不能共存,写写不能共存。
锁降级:从写锁变成读锁;
锁升级:从读锁变成写锁。
ReentrantReadWriteLock不支持锁升级,支持锁降级
ReadWriteLock rtLock = new ReentrantReadWriteLock(); rtLock.readLock().lock(); System.out.println("get readLock."); rtLock.writeLock().lock(); System.out.println("blocking");
会死锁
ReadWriteLock rtLock = new ReentrantReadWriteLock(); rtLock.writeLock().lock(); System.out.println("writeLock"); rtLock.readLock().lock(); System.out.println("get read lock");
不会死锁
案例应用:
import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; public class CacheDemo { /** * 缓存器,这里假设需要存储1000左右个缓存对象,按照默认的负载因子0.75,则容量=750,大概估计每一个节点链表长度为5个 * 那么数组长度大概为:150,又有雨设置map大小一般为2的指数,则最近的数字为:128 */ private Map<String, Object> map = new HashMap<>(128); private ReadWriteLock rwl = new ReentrantReadWriteLock(); public static void main(String[] args) { } public Object get(String id){ Object value = null; rwl.readLock().lock();//首先开启读锁,从缓存中去取 try{ value = map.get(id); if(value == null){ //如果缓存中没有释放读锁,上写锁 rwl.readLock().unlock(); rwl.writeLock().lock(); try{ if(value == null){ //防止多写线程重复查询赋值 value = "redis-value"; //此时可以去数据库中查找,这里简单的模拟一下 } rwl.readLock().lock(); //加读锁降级写锁,不明白的可以查看上面锁降级的原理与保持读取数据原子性的讲解 }finally{ rwl.writeLock().unlock(); //释放写锁 } } }finally{ rwl.readLock().unlock(); //最后释放读锁 } return value; } }
如果不使用锁降级功能,如先释放写锁,然后获得读锁,在这个get过程中,可能会有其他线程竞争到写锁 或者是更新数据 则获得的数据是其他线程更新的数据,可能会造成数据的污染,即产生脏读的问题。