code.7z
一、介绍
上一篇提到使用 Single Thread Execution 来解决共享资源的线程安全问题(线程安全问题基本上就是数据不一致问题)。
对于共享资源的操作分为两种:读操作、写操作。
针对读写操作,多线程进行共享资源的操作会出现数据不一致存在几种情况:
线程 | 读 | 写 |
---|---|---|
读 | 数据一致 | 数据不一致 |
写 | 数据不一致 | 数据不一致 |
如上述表格的四种情况,针对多线程间的读操作不会造成数据不一致的问题。
Single Thread Execution 模式,锁定共享资源针对的是所有的操作,也包括线程间的读操作。
如果能够在此基础上进行优化,针对多线程间的读操作,不进行加锁处理,可以在一定程度上提高性能。
在读多写少的情况下,性能的提升将会更明显。
这就是 读写锁分离设计模式
二、实现
2.1、思路
锁的实现以读操作和写操作进行分离。虽然读写锁实现分离,但是两者争抢的仍然是同一个锁。
针对读锁:只要当前没有在进行写操作的线程,都能够获取到读锁。
针对写锁:只有当前没有任何线程在进行读写操作,才能够获取到写锁。
2.2、具体实现
如上图,代码实现类
- Lock:接口,定义了读锁、和写锁的接口定义
- ReadLock:读锁实现
- WriteLock:写锁实现
- ReadWriteLock:读写锁接口定义,整合读锁和写锁。
- ReadWriteLockImpl:读写锁实现类
2.2.1、读写锁实现ReadWriteLockImpl
关键代码
代码中定义了
- 读写锁实现时实际争抢的锁-对象锁
METEX
- 当前等待读写操作,以及正在进行读写操作的线程数,用以控制读写锁是否运行进行操作。
2.2.2、写锁实现 WriteLock
关键代码
获取锁 WriteLock#lock
释放锁 WriteLock#unlock
2.2.3、读锁实现 ReadLock
关键代码
获取锁 ReadLock#lock
三、总结
线程争抢锁的操作逻辑图如下:
使用如 synchronized 关键字的锁,针对共享资源不区分读写操作,都是直接进行加锁,同一个时刻只能有一个线程对共享资源进行操作。
读写锁分离本质上也是一把锁,不同于 synchronized 等锁,读写锁将一把锁分离成两种状态:读和写。
再来回顾一下,针对共享资源,多线程同时执行,数据一致性存在的几种情况
线程 | 读 | 写 |
---|---|---|
读 | 数据一致 | 数据不一致 |
写 | 数据不一致 | 数据不一致 |
多线程中,锁存在的意义就是保证共享资源的数据一致性,而针对读操作来说,同一时刻即使多线程同时操作也不会影响到数据一致性, 读写锁分离要做的就是针对多线程的读操作进行放行,减少某一个状态下锁性能的损耗,达到提升性能的效果。