java读写锁解读

简介: java读写锁解读

读写锁介绍

现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那 么频繁。在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以 应该允许多个线程同时读取共享资源;但是如果一个线程想去写这些共享资源, 就不应该允许其他线程对该资源进行读和写的操作了。

针对这种场景,JAVA 的并发包提供了读写锁 ReentrantReadWriteLock, 它表示两个锁,一个是读操作相关的锁,称为共享锁;一个是写相关的锁,称为排他锁

1. 线程进入读锁的前提条件:

• 没有其他线程的写锁

• 没有写请求, 或者有写请求,但调用线程和持有锁的线程是同一个(可重入锁)。

2. 线程进入写锁的前提条件:

• 没有其他线程的读锁

• 没有其他线程的写锁

而读写锁有以下三个重要的特性:

(1)公平选择性:支持非公平(默认)和公平的锁获取方式,吞吐量还是非公平优于公平。

(2)重进入:读锁和写锁都支持线程重进入。

(3)锁降级:遵循获取写锁、获取读锁再释放写锁的次序,写锁能够降级成为 读锁。

ReentrantReadWriteLock

1. public class ReentrantReadWriteLock implements ReadWriteLock,
2.         java.io.Serializable {
3. /**
4.      * 读锁
5.      */
6. private final ReentrantReadWriteLock.ReadLock readerLock;
7. 
8. /**
9.      * 写锁
10.      */
11. private final ReentrantReadWriteLock.WriteLock writerLock;
12. final Sync sync;
13. 
14. /**
15.      * 使用默认(非公平)的排序属性创建一个新的
16.      * ReentrantReadWriteLock
17.      */
18. public ReentrantReadWriteLock() {
19. this(false);
20.     }
21. 
22. /**
23.      * 使用给定的公平策略创建一个新的 ReentrantReadWriteLock
24.      */
25. public ReentrantReadWriteLock(boolean fair) {
26.         sync = fair ? new FairSync() : new NonfairSync();
27.         readerLock = new ReadLock(this);
28.         writerLock = new WriteLock(this);
29. 
30. 
31.     }
32. 
33. /**
34.      * 返回用于写入操作的锁
35.      */
36. public ReentrantReadWriteLock.WriteLock writeLock() {
37. return
38.                 writerLock;
39.     }
40. 
41. /**
42.      * 返回用于读取操作的锁
43.      */
44. public ReentrantReadWriteLock.ReadLock readLock() {
45. return
46.                 readerLock;
47.     }
48. 
49. abstract static class Sync extends AbstractQueuedSynchronizer {
50.     }
51. 
52. static final class NonfairSync extends Sync {
53.     }
54. 
55. static final class FairSync extends Sync {
56.     }
57. 
58. public static class ReadLock implements Lock, java.io.Serializable {
59.     }
60. 
61. public static class WriteLock implements Lock, java.io.Serializable {
62.     }
63. }

可以看到,ReentrantReadWriteLock 实现了 ReadWriteLock 接口, ReadWriteLock 接口定义了获取读锁和写锁的规范,具体需要实现类去实现; 同时其还实现了 Serializable 接口,表示可以进行序列化,在源代码中可以看 到 ReentrantReadWriteLock 实现了自己的序列化逻辑。  

入门案例

场景: 使用 ReentrantReadWriteLock 对一个 hashmap 进行读和写操作

1. class  MyCache{
2. //创建map集合
3. private  volatile Map<String,Object> map=new HashMap<>();
4. 
5. //创建读写锁对象
6. private ReadWriteLock rw=new ReentrantReadWriteLock();
7. //放对象
8. public void put(String key,Object value){
9.          rw.writeLock().lock();
10. try {
11.              System.out.println(Thread.currentThread().getName()+"正在进行操作");
12.              TimeUnit.MICROSECONDS.sleep(300);
13.              map.put(key,value);
14.              System.out.println("操作完成");
15.          } catch (InterruptedException e) {
16.              e.printStackTrace();
17.          } finally {
18.            rw.writeLock().unlock();
19.          }
20.      }
21. //取对象
22. public Object get(String key){
23.          Object result=null;
24. try {
25.              rw.readLock().lock();
26.              System.out.println(Thread.currentThread().getName() + "正在取值");
27.               TimeUnit.MICROSECONDS.sleep(300);
28.               result=map.get(key);
29.          } catch (InterruptedException e) {
30.              e.printStackTrace();
31.          }finally {
32.              rw.readLock().unlock();
33.          }
34. return  result;
35.     }
36. }

小结

• 在线程持有读锁的情况下,该线程不能取得写锁(因为获取写锁的时候,如果发 现当前的读锁被占用,就马上获取失败,不管读锁是不是被当前线程持有)。

• 在线程持有写锁的情况下,该线程可以继续获取读锁(获取读锁时如果发现写 锁被占用,只有写锁没有被当前线程占用的情况才会获取失败)。

原因: 当线程获取读锁的时候,可能有其他线程同时也在持有读锁,因此不能把 获取读锁的线程“升级”为写锁;而对于获得写锁的线程,它一定独占了读写锁,因此可以继续让它获取读锁,当它同时获取了写锁和读锁后,还可以先释 放写锁继续持有读锁,这样一个写锁就“降级”为了读锁。


相关文章
|
7天前
|
安全 Java 调度
Java编程时多线程操作单核服务器可以不加锁吗?
Java编程时多线程操作单核服务器可以不加锁吗?
21 2
|
2月前
|
存储 Oracle 安全
揭秘Java并发核心:深入Hotspot源码腹地,彻底剖析Synchronized关键字的锁机制与实现奥秘!
【8月更文挑战第4天】在Java并发世界里,`Synchronized`如同导航明灯,确保多线程环境下的代码安全执行。它通过修饰方法或代码块实现独占访问。在Hotspot JVM中,`Synchronized`依靠对象监视器(Object Monitor)机制实现,利用对象头的Mark Word管理锁状态。
42 1
|
21天前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)
|
9天前
|
算法 Java 关系型数据库
Java中到底有哪些锁
【9月更文挑战第24天】在Java中,锁主要分为乐观锁与悲观锁、自旋锁与自适应自旋锁、公平锁与非公平锁、可重入锁以及独享锁与共享锁。乐观锁适用于读多写少场景,通过版本号或CAS算法实现;悲观锁适用于写多读少场景,通过加锁保证数据一致性。自旋锁与自适应自旋锁通过循环等待减少线程挂起和恢复的开销,适用于锁持有时间短的场景。公平锁按请求顺序获取锁,适合等待敏感场景;非公平锁性能更高,适合频繁加解锁场景。可重入锁支持同一线程多次获取,避免死锁;独享锁与共享锁分别用于独占和并发读场景。
|
17天前
|
Java 数据库
JAVA并发编程-一文看懂全部锁机制
曾几何时,面试官问:java都有哪些锁?小白,一脸无辜:用过的有synchronized,其他不清楚。面试官:回去等通知! 今天我们庖丁解牛说说,各种锁有什么区别、什么场景可以用,通俗直白的分析,让小白再也不怕面试官八股文拷打。
|
17天前
|
安全 Java 开发者
Java并发编程中的锁机制解析
本文深入探讨了Java中用于管理多线程同步的关键工具——锁机制。通过分析synchronized关键字和ReentrantLock类等核心概念,揭示了它们在构建线程安全应用中的重要性。同时,文章还讨论了锁机制的高级特性,如公平性、类锁和对象锁的区别,以及锁的优化技术如锁粗化和锁消除。此外,指出了在高并发环境下锁竞争可能导致的问题,并提出了减少锁持有时间和使用无锁编程等策略来优化性能的建议。最后,强调了理解和正确使用Java锁机制对于开发高效、可靠并发应用程序的重要性。
16 3
|
2月前
|
存储 Java
Java锁是什么?简单了解
在高并发环境下,锁是Java中至关重要的概念。锁或互斥是一种同步机制,用于限制多线程环境下的资源访问,确保排他性和并发控制。例如,超市储物柜仅能存放一个物品,若三人同时使用,则需通过锁机制确保每次只有一个线程访问。Java中可以通过`synchronized`关键字实现加锁,确保关键代码段的原子性,避免数据不一致问题。正确使用锁可有效提升程序的稳定性和安全性。
Java锁是什么?简单了解
|
2月前
|
小程序 Java 开发工具
【Java】@Transactional事务套着ReentrantLock锁,锁竟然失效超卖了
本文通过一个生动的例子,探讨了Java中加锁仍可能出现超卖问题的原因及解决方案。作者“JavaDog程序狗”通过模拟空调租赁场景,详细解析了超卖现象及其背后的多线程并发问题。文章介绍了四种解决超卖的方法:乐观锁、悲观锁、分布式锁以及代码级锁,并重点讨论了ReentrantLock的使用。此外,还分析了事务套锁失效的原因及解决办法,强调了事务边界的重要性。
58 2
【Java】@Transactional事务套着ReentrantLock锁,锁竟然失效超卖了
|
1月前
|
Oracle Java 关系型数据库
【颠覆性升级】JDK 22:超级构造器与区域锁,重塑Java编程的两大基石!
【9月更文挑战第6天】JDK 22的发布标志着Java编程语言在性能和灵活性方面迈出了重要的一步。超级构造器和区域锁这两大基石的引入,不仅简化了代码设计,提高了开发效率,还优化了垃圾收集器的性能,降低了应用延迟。这些改进不仅展示了Oracle在Java生态系统中的持续改进和创新精神,也为广大Java开发者提供了更多的可能性和便利。我们有理由相信,在未来的Java编程中,这些新特性将发挥越来越重要的作用,推动Java技术不断向前发展。
|
2月前
|
Java 开发者
Java多线程教程:使用ReentrantLock实现高级锁功能
Java多线程教程:使用ReentrantLock实现高级锁功能
34 1
下一篇
无影云桌面