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. }

小结

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

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

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


相关文章
|
6月前
|
存储 架构师 安全
深入理解Java锁升级:无锁 → 偏向锁 → 轻量级锁 → 重量级锁(图解+史上最全)
锁状态bits1bit是否是偏向锁2bit锁标志位无锁状态对象的hashCode001偏向锁线程ID101轻量级锁指向栈中锁记录的指针000重量级锁指向互斥量的指针010尼恩提示,讲完 如减少锁粒度、锁粗化、关闭偏向锁(-XX:-UseBiasedLocking)等优化手段 , 可以得到 120分了。如减少锁粒度、锁粗化、关闭偏向锁(-XX:-UseBiasedLocking)等‌。JVM锁的膨胀、锁的内存结构变化相关的面试题,是非常常见的面试题。也是核心面试题。
深入理解Java锁升级:无锁 → 偏向锁 → 轻量级锁 → 重量级锁(图解+史上最全)
|
12月前
|
安全 Java 调度
Java编程时多线程操作单核服务器可以不加锁吗?
Java编程时多线程操作单核服务器可以不加锁吗?
134 2
|
7月前
|
Java API 数据处理
深潜数据海洋:Java文件读写全面解析与实战指南
通过本文的详细解析与实战示例,您可以系统地掌握Java中各种文件读写操作,从基本的读写到高效的NIO操作,再到文件复制、移动和删除。希望这些内容能够帮助您在实际项目中处理文件数据,提高开发效率和代码质量。
168 4
|
11月前
|
Java
Java 中锁的主要类型
【10月更文挑战第10天】
263 59
|
10月前
|
缓存 Java
java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁
本文介绍了几种常见的锁机制,包括公平锁与非公平锁、可重入锁与不可重入锁、自旋锁以及读写锁和互斥锁。公平锁按申请顺序分配锁,而非公平锁允许插队。可重入锁允许线程多次获取同一锁,避免死锁。自旋锁通过循环尝试获取锁,减少上下文切换开销。读写锁区分读锁和写锁,提高并发性能。文章还提供了相关代码示例,帮助理解这些锁的实现和使用场景。
259 4
java中的公平锁、非公平锁、可重入锁、递归锁、自旋锁、独占锁和共享锁
|
10月前
|
Java 开发者
Java 中的锁是什么意思,有哪些分类?
在Java多线程编程中,锁用于控制多个线程对共享资源的访问,确保数据一致性和正确性。本文探讨锁的概念、作用及分类,包括乐观锁与悲观锁、自旋锁与适应性自旋锁、公平锁与非公平锁、可重入锁和读写锁,同时提供使用锁时的注意事项,帮助开发者提高程序性能和稳定性。
430 3
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)
|
11月前
|
XML JavaScript Java
java与XML文件的读写
java与XML文件的读写
144 3
|
小程序 Java 开发工具
【Java】@Transactional事务套着ReentrantLock锁,锁竟然失效超卖了
本文通过一个生动的例子,探讨了Java中加锁仍可能出现超卖问题的原因及解决方案。作者“JavaDog程序狗”通过模拟空调租赁场景,详细解析了超卖现象及其背后的多线程并发问题。文章介绍了四种解决超卖的方法:乐观锁、悲观锁、分布式锁以及代码级锁,并重点讨论了ReentrantLock的使用。此外,还分析了事务套锁失效的原因及解决办法,强调了事务边界的重要性。
304 2
【Java】@Transactional事务套着ReentrantLock锁,锁竟然失效超卖了
|
存储 Java
Java锁是什么?简单了解
在高并发环境下,锁是Java中至关重要的概念。锁或互斥是一种同步机制,用于限制多线程环境下的资源访问,确保排他性和并发控制。例如,超市储物柜仅能存放一个物品,若三人同时使用,则需通过锁机制确保每次只有一个线程访问。Java中可以通过`synchronized`关键字实现加锁,确保关键代码段的原子性,避免数据不一致问题。正确使用锁可有效提升程序的稳定性和安全性。
259 1
Java锁是什么?简单了解

热门文章

最新文章