说说ReentrantReadWriteLock
1) 背景
jni的使用场景,多线程读,单线程写,写的时候会更新java对象,当老的java对象无须再使用的时候必须释放jni所占用本地方法区的内存,这个区域的内存不在java heap范畴,因此也无法被垃圾回收掉,需要显式的释放。
但问题在于什么释放?
有人会说使用finalize,但finalize过于依赖jvm的回收的时机,这使得什么时候能真正释放显得不太好预测。
或者使用synchronized 内部锁,这样会导致性能的下降,为了极少量的写牺牲了大量的读。
释放的时机确实不太好把握,因为必须等待所有对于老的java对象的读线程访问完毕才能释放,否者jvm会崩溃。
恰好ReentrantReadWriteLock可以满足这个要求
2)ReentrantReadWriteLock竞争条件
ReentrantReadWriteLock会使用两把锁来解决问题,一个读锁,一个写锁
线程进入读锁的前提条件:
没有其他线程的写锁,
没有写请求或者有写请求,但调用线程和持有锁的线程是同一个
线程进入写锁的前提条件:
没有其他线程的读锁
没有其他线程的写锁
2)样例代码
- 。。。。。。
- private SomeClass someClass; //锁的资源
- private final ReadWriteLock lock = new ReentrantReadWriteLock();
- private final Lock r = lock.readLock();
- private final Lock w = lock.writeLock();
- 。。。。。。
- //读方法
- 。。。。。。
- r.lock();
- try {
- result = someClass.someMethod();
- } catch (Exception e) {
- // process
- } finally {
- r.unlock();
- }
- 。。。。。。。
- //写方法
- 。。。。。。
- //产生新的SomeClass实例tempSomeClass
- 。。。。。。。
- w.lock();
- try{
- //释放老的资源
- this.someClass.dispose();
- //更新成新的实例
- this.someClass = tempSomeClass;
- }finally{
- w.unlock();
- }
- 。。。。。。。
很简单的代码就解决了我们的问题
4)性能测试
接下来对比下使用ReentrantReadWriteLock和不使用任何锁的性能比较情况:
我们使用100个读线程并发进行压力测试,发现在100%读的情况,性能没有任何损失,
之后我们在100个读线程的基础上加了一个写线程,每分钟写一次,性能几乎没有损失。
5)小结
使用ReentrantReadWriteLock可以推广到大部分读,少量写的场景,因为读线程之间没有竞争,所以比起sychronzied,性能好很多。
如果需要较为精确的控制缓存,使用ReentrantReadWriteLock倒也不失为一个方案。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架