Java使用读写锁替代同步锁

字数 1095阅读 436

应用情景

前一阵有个做反抄袭检测的小伙伴问了我一个问题。

---

在多线程里就是有个变量,我需要读取它来判断是否给它写入一些信息。

打算加锁,但是如果读取时候加入readlock,写入时候加入writelock,

这样做可能读写不同步。但是如果一起加lock效果就跟synchronized一样,效率变差

---

其实他的问题就是下面的场景:

高并发的读写请求

读取请求明显大于写入的请求

如果用synchronized同步锁会导致性能下降,本来读取是可以多线程同步进行的,同步锁就只能让他们一个一个排队读取。

如果读取时加readlock,写入时候加writelock,会提升效率,因为读可以多线程并发,但是在线程A读完上锁的毫秒级时间里,有可能线程B也读完了,而且抢在了线程A之前修改了变量,导致程序出错。

处理方案

我去研究了下读写锁,后来发现其实java官方已经给了答案。使用读写锁加标志位解决读写不同步的问题。Java ReentrantReadWriteLock

在这先普及下读写锁。

读锁:

读锁拒绝其他线程获得写锁,读锁不拒绝其他线程获得读锁,多个上了读锁的线程可以并发读不会阻塞。

多个读锁同时作用期间,其他想上写锁的线程都处在等待状态,当最后一个读锁释放后,才有可能上锁。

写锁:

写锁拒绝其他线程获取读锁和写锁。

当一个线程获取写锁后,其他想要获取读写锁的线程都处于等待状态,直到写锁释放才有可能上锁。

##代码实例

直接拿Java官方示例解读了。

classCachedData{Objectdata;  volatile boolean cacheValid;  final ReentrantReadWriteLock rwl =newReentrantReadWriteLock();voidprocessCachedData() {    rwl.readLock().lock();//1. 上读锁if(!cacheValid) {//2. 验证cacheValid// Must release read lock before acquiring write lockrwl.readLock().unlock();//3. 解除读锁rwl.writeLock().lock();//4. 上写锁try{// Recheck state because another thread might have// acquired write lock and changed state before we did.if(!cacheValid) {//5. 验证cacheValiddata = ...            cacheValid =true;          }// Downgrade by acquiring read lock before releasing write lockrwl.readLock().lock();//6. 上读锁}finally{          rwl.writeLock().unlock();// Unlock write, still hold read //7. 解除写锁}    }try{      use(data);    }finally{      rwl.readLock().unlock();//8. 解除读锁}  } }

比如现在有线程ABCDE五个线程,使用processCachedData()方法,接下来会发现如下步骤。

-> DE线程滞后,ABC同时进入到步骤1. 上读锁

-> ABC进入到步骤2. 验证cacheValid。(新实例中cacheValid初始为fasle,所以进入if条件句中)

-> ABC执行步骤3. 解除读锁。

-> 假设此时A率先完成步骤4. 上写锁。

-> BC无法获取写锁,处于等待状态,被阻塞在步骤4。 上写锁。此时D线程执行使用processCachedData()方法,被阻塞在步骤1. 上读锁。

-> A进入到步骤5. 验证cacheValid。

步骤五很关键,如果线程A写完后,解除了写锁,此时新的线程E获取到了写锁,就会写入新数据,此时就不是同步锁了,程序出错。

-> A修改数据,cacheValid置为true。步骤6和步骤7是写锁的降级操作,即写锁释放的时候,先降级为读锁,这样其他等待获取写锁的线程会继续等待,然后再释放写锁,保证同步性。

-> A执行完步骤8,BC阻塞结束,其中一位获取写锁。

如果你在学习Java的过程中或者在工作中遇到什么问题都可以来群里提问,阿里Java高级大牛直播讲解知识点,分享知识,多年工作经验的梳理和总结,带着大家全面、科学地建立自己的技术体系和技术认知!可以加群找我要课堂链接 注意:是免费的 没有开发经验误入哦! 非喜勿入! 学习交流QQ群:478052716

推荐阅读更多精彩内容