`

ReentrantReadWriteLock

    博客分类:
  • java
阅读更多

(转载http://blog.csdn.net/pwlazy/article/details/5640286

 

1) 背景
    jni的使用场景,多线程读,单线程写,写的时候会更新java对象,当老的java对象无须再使用的时候必须释放jni所占用本地方法区的内存,这个区域的内存不在java heap范畴,因此也无法被垃圾回收掉,需要显式的释放。

但问题在于什么释放?

有人会说使用finalize,但finalize过于依赖jvm的回收的时机,这使得什么时候能真正释放显得不太好预测。

或者使用synchronized 内部锁,这样会导致性能的下降,为了极少量的写牺牲了大量的读。

释放的时机确实不太好把握,因为必须等待所有对于老的java对象的读线程访问完毕才能释放,否者jvm会崩溃。

恰好ReentrantReadWriteLock可以满足这个要求


2)ReentrantReadWriteLock竞争条件

ReentrantReadWriteLock会使用两把锁来解决问题,一个读锁,一个写锁

线程进入读锁的前提条件:
    没有其他线程的写锁,
    没有写请求或者有写请求,但调用线程和持有锁的线程是同一个

线程进入写锁的前提条件:
    没有其他线程的读锁
    没有其他线程的写锁

2)样例代码

 

[java] view plaincopy
  1. 。。。。。。  
  2. private SomeClass someClass;  //锁的资源  
  3. private final ReadWriteLock lock = new ReentrantReadWriteLock();  
  4. private final Lock r = lock.readLock();  
  5. private final Lock w = lock.writeLock();  
  6. 。。。。。。  
  7. //读方法  
  8. 。。。。。。  
  9. r.lock();  
  10. try {  
  11.     result = someClass.someMethod();  
  12. catch (Exception e) {  
  13.     // process  
  14. finally {  
  15.     r.unlock();  
  16. }  
  17. 。。。。。。。  
  18. //写方法  
  19. 。。。。。。  
  20. //产生新的SomeClass实例tempSomeClass  
  21. 。。。。。。。  
  22.          
  23. w.lock();  
  24. try{  
  25.     //释放老的资源  
  26.     this.someClass.dispose();  
  27.     //更新成新的实例  
  28.     this.someClass = tempSomeClass;  
  29. }finally{  
  30.     w.unlock();  
  31. }  
  32. 。。。。。。。  



很简单的代码就解决了我们的问题

 

 

4)性能测试

接下来对比下使用ReentrantReadWriteLock和不使用任何锁的性能比较情况:
我们使用100个读线程并发进行压力测试,发现在100%读的情况,性能没有任何损失,

之后我们在100个读线程的基础上加了一个写线程,每分钟写一次,性能几乎没有损失。

5)小结
使用ReentrantReadWriteLock可以推广到大部分读,少量写的场景,因为读线程之间没有竞争,所以比起sychronzied,性能好很多。
如果需要较为精确的控制缓存,使用ReentrantReadWriteLock倒也不失为一个方案。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics