当前位置:   article > 正文

线程基础(九)ReentrantReadWriteLock ,synchronized和ReentrantLock的对比_reentrantlock和readwritelock哪个效率好

reentrantlock和readwritelock哪个效率好

读写锁ReentrantReadWriteLock概述

大型网站中很重要的一块内容就是数据的读写,ReentrantLock虽然具有完全互斥排他的效果(即同一时间只有一个线程正在执行lock后面的任务),但是效率非常低。所以在JDK中提供了一种读写锁ReentrantReadWriteLock,使用它可以加快运行效率。

读写锁表示两个锁,一个是读操作相关的锁,称为共享锁;另一个是写操作相关的锁,称为排他锁。我把这两个操作理解为三句话:

1、读和读之间不互斥,因为读操作不会有线程安全问题

2、写和写之间互斥,避免一个写操作影响另外一个写操作,引发线程安全问题

3、读和写之间互斥,避免读操作的时候写操作修改了内容,引发线程安全问题

总结起来就是,多个Thread可以同时进行读取操作,但是同一时刻只允许一个Thread进行写入操作

读和读共享

先证明一下第一句话"读和读之间不互斥"

  1. public class Test06 {
  2. ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
  3. public void methodA(){
  4. try {
  5. lock.readLock().lock();
  6. System.out.println(Thread.currentThread().getName()+"获取锁");
  7. Thread.sleep(Integer.MAX_VALUE);
  8. } catch (Exception e) {
  9. e.printStackTrace();
  10. }finally{
  11. lock.readLock().unlock();
  12. }
  13. }
  14. public static void main(String[] args) {
  15. final Test06 t = new Test06();
  16. Runnable r = new Runnable() {
  17. @Override
  18. public void run() {
  19. t.methodA();
  20. }
  21. };
  22. Thread t1 = new Thread(r,"线程1");
  23. Thread t2 = new Thread(r,"线程2");
  24. t1.start();
  25. t2.start();
  26. }
  27. }
  1. 线程1获取锁
  2. 线程2获取锁

如上诉代码,当第一个线程获取锁以后,让它进行长时间休眠。如果互斥则线程2应该无法获取锁,所以读和读之间不互斥

写和写互斥

改写上方代码

  1. public class Test06 {
  2. ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
  3. public void methodA(){
  4. try {
  5. // lock.readLock().lock();
  6. lock.writeLock().lock();
  7. System.out.println(Thread.currentThread().getName()+"获取锁");
  8. Thread.sleep(Integer.MAX_VALUE);
  9. } catch (Exception e) {
  10. e.printStackTrace();
  11. }finally{
  12. lock.writeLock().unlock();
  13. }
  14. }
  15. public static void main(String[] args) {
  16. final Test06 t = new Test06();
  17. Runnable r = new Runnable() {
  18. @Override
  19. public void run() {
  20. t.methodA();
  21. }
  22. };
  23. Thread t1 = new Thread(r,"线程1");
  24. Thread t2 = new Thread(r,"线程2");
  25. t1.start();
  26. t2.start();
  27. }
  28. }
线程1获取锁

当线程1获取锁以后,释放前线程2获取不到锁。证明写和写之间互斥

读和写互斥

最后证明一下第三句话"读和写之间互斥",证明方法无非是把上面二者结合起来而已

  1. public class Test07 {
  2. ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
  3. public void write(){
  4. try {
  5. lock.writeLock().lock();
  6. System.out.println(Thread.currentThread().getName()+"获取锁");
  7. Thread.sleep(Integer.MAX_VALUE);
  8. } catch (Exception e) {
  9. e.printStackTrace();
  10. }finally{
  11. lock.writeLock().unlock();
  12. }
  13. }
  14. public void read(){
  15. try {
  16. lock.readLock().lock();
  17. System.out.println(Thread.currentThread().getName()+"获取锁");
  18. Thread.sleep(Integer.MAX_VALUE);
  19. } catch (Exception e) {
  20. e.printStackTrace();
  21. }finally{
  22. lock.readLock().unlock();
  23. }
  24. }
  25. public static void main(String[] args) {
  26. final Test07 t = new Test07();
  27. Thread t1 = new Thread(new Runnable() {
  28. @Override
  29. public void run() {
  30. t.write();
  31. }
  32. },"线程1");
  33. Thread t2 = new Thread(new Runnable() {
  34. @Override
  35. public void run() {
  36. t.write();
  37. }
  38. },"线程2");
  39. t1.start();
  40. t2.start();
  41. }
  42. }
线程1获取锁

当线程1获取锁以后,释放前线程2获取不到锁。证明读和写之间互斥

 

synchronized和ReentrantLock的对比

到现在,看到多线程中,锁定的方式有2种:synchronized和ReentrantLock。两种锁定方式各有优劣,下面简单对比一下:

1、synchronized是关键字,就和if...else...一样,是语法层面的实现,因此synchronized获取锁以及释放锁都是Java虚拟机帮助用户完成的;ReentrantLock是类层面的实现,因此锁的获取以及锁的释放都需要用户自己去操作。特别再次提醒,ReentrantLock在lock()完了,一定要手动unlock()

2、synchronized简单,简单意味着不灵活,而ReentrantLock的锁机制给用户的使用提供了极大的灵活性。这点在Hashtable和ConcurrentHashMap中体现得淋漓尽致。synchronized一锁就锁整个Hash表,而ConcurrentHashMap则利用ReentrantLock实现了锁分离,锁的知识segment而不是整个Hash表

3、synchronized是不公平锁,而ReentrantLock可以指定锁是公平的还是非公平的

4、synchronized实现等待/通知机制通知的线程是随机的,ReentrantLock实现等待/通知机制可以有选择性地通知

5、和synchronized相比,ReentrantLock提供给用户多种方法用于锁信息的获取,比如可以知道lock是否被当前线程获取、lock被同一个线程调用了几次、lock是否被任意线程获取等等

6. ReentrantLock的某些方法可以决定多长时间内尝试获取锁,如果获取不到就抛异常,这样就可以一定程度上减轻死锁的可能性,如果锁被另一个线程占据了,synchronized只会一直等待,很容易错序死锁

7.synchronized的话,锁的范围是整个方法或synchronized块部分;而Lock因为是方法调用,可以跨方法,灵活性更大

 

总结起来,我认为如果只需要锁定简单的方法、简单的代码块,那么考虑使用synchronized,复杂的多线程处理场景下可以考虑使用ReentrantLock。当然这只是建议性地,还是要具体场景具体分析的。

JDK1.5版本只有由于对synchronized做了诸多优化,效率上synchronized和ReentrantLock应该是差不多。

 

 

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/271696
推荐阅读
相关标签
  

闽ICP备14008679号