code.7z

一、介绍

上一篇提到使用 Single Thread Execution 来解决共享资源的线程安全问题(线程安全问题基本上就是数据不一致问题)。

对于共享资源的操作分为两种:读操作、写操作。

针对读写操作,多线程进行共享资源的操作会出现数据不一致存在几种情况:

线程
数据一致 数据不一致
数据不一致 数据不一致

如上述表格的四种情况,针对多线程间的读操作不会造成数据不一致的问题。

Single Thread Execution 模式,锁定共享资源针对的是所有的操作,也包括线程间的读操作。

如果能够在此基础上进行优化,针对多线程间的读操作,不进行加锁处理,可以在一定程度上提高性能。

在读多写少的情况下,性能的提升将会更明显。

这就是 读写锁分离设计模式

二、实现

2.1、思路

锁的实现以读操作和写操作进行分离。虽然读写锁实现分离,但是两者争抢的仍然是同一个锁。

针对读锁:只要当前没有在进行写操作的线程,都能够获取到读锁。

针对写锁:只有当前没有任何线程在进行读写操作,才能够获取到写锁。

2.2、具体实现

01.png
如上图,代码实现类

  • Lock:接口,定义了读锁、和写锁的接口定义
    02.png
  • ReadLock:读锁实现
  • WriteLock:写锁实现
  • ReadWriteLock:读写锁接口定义,整合读锁和写锁。
    03.png
  • ReadWriteLockImpl:读写锁实现类

2.2.1、读写锁实现ReadWriteLockImpl 关键代码

04.png
代码中定义了

  • 读写锁实现时实际争抢的锁-对象锁 METEX
  • 当前等待读写操作,以及正在进行读写操作的线程数,用以控制读写锁是否运行进行操作。

2.2.2、写锁实现 WriteLock 关键代码

获取锁 WriteLock#lock
05.png
释放锁 WriteLock#unlock
06.png

2.2.3、读锁实现 ReadLock 关键代码

获取锁 ReadLock#lock

释放锁 ReadLock#unlock
07.png

三、总结

线程争抢锁的操作逻辑图如下:
08.png
使用如 synchronized 关键字的锁,针对共享资源不区分读写操作,都是直接进行加锁,同一个时刻只能有一个线程对共享资源进行操作。

读写锁分离本质上也是一把锁,不同于 synchronized 等锁,读写锁将一把锁分离成两种状态:读和写。

再来回顾一下,针对共享资源,多线程同时执行,数据一致性存在的几种情况

线程
数据一致 数据不一致
数据不一致 数据不一致

多线程中,锁存在的意义就是保证共享资源的数据一致性,而针对读操作来说,同一时刻即使多线程同时操作也不会影响到数据一致性, 读写锁分离要做的就是针对多线程的读操作进行放行,减少某一个状态下锁性能的损耗,达到提升性能的效果。