第6章-ReadWriteLock
分类:
文章
•
2024-07-28 09:59:40
6.1 模式简介
- 学生们正在一起看老师在黑板上写的板书。这时,老师想擦掉板书,再写新的内容。而学生们说道:“老师,我们还没看完,请先不要擦掉!” 于是,老师就会等待大家都看完。
- 当线程 “读取” 实例的状态时,实例的状态不会发生变化。实例的状态仅在线程执行 “写入” 操作时才会发生变化。从实例的状态变化这个观点来看,“读取” 和 “写入” 有着本质的区别。
- 在Read-Write Lock模式中,读取操作和写入操作是分开考虑的。在执行读取操作之前,线程必须获取用于读取的锁。而在执行写入操作之前,线程必须获取用于写入的锁。
- 由于当线程执行读取操作时,实例的状态不会发生变化,所以多个线程可以同时读取。但在读取时,不可以写入。
- 当线程执行写入操作时,实例的状态就会发生变化。因此,当有一个线程正在写人时,其他线程不可以读取或写入。
6.3 Read-Write Lock模式中的角色
6.3.1 Reader(读者)
- Reader 角色对 SharedResource 角色执行 read 操作。
6.3.2 Writer(写者)
- Writer 角色对 SharedResource 角色执行 write 操作。
6.3.3 SharedResource(共享资源)
- SharedResource角色表示的是 Reader 角色和 Writer 角色二者共享的资源。SharedResource角色提供不修改内部状态的操作(read)和修改内部状态的操作(write)。
6.3.4 ReadWriteLock(读写锁)
- ReadWriteLock角色提供了SharedResource角色实现read操作和write操作时所需的锁。即实现read操作时所需的 readLock 和 readUnlock,以及实现write操作时所需的 writeLock和writeUnlock。
6.3.5 类图和Timethreads图
6.4 拓展思路的要点
6.4.1 利用“读取”操作的线程之间不会冲突的特性来提高程序性能
- Read-Write Lock模式利用了读取操作的线程之间不会冲突的特性。由于读取操作不会修改SharedResource角色的状态,所以彼此之间无需执行互斥处理。因此多个Reader角色可以同时执行read操作,从而提高程序性能。
- 但性能的提升也不是这么绝对。Read-Write Lock模式能否提高程序性能,必须通过实际的测量来判定。另外,还需要考虑下面讲述的 “适合读取操作繁重时” 和 “适合读取频率比写入频率高时” 这两个大方针。
6.4.2 适合读取操作繁重时
- 在单纯使用Single Threaded Execution模式的情况下,就算是read操作,每次也只能运行一个线程。如果read的操作很繁重(耗费时间),那么使用Read-Write Lock模式比使用Single Threaded Execution 模式更加合适。
- 但是,因为Read-Write Lock模式的处理比Single Threaded Execution模式复杂,所以当read的操作很简单(不耗费时间)时,Single Threaded Execution模式反而会更加合适。
6.4.3 适合读取频率比写入频率高时
- Read-Write Lock模式的优点是Reader角色之间不会发生冲突。但是,如果写入处理(write)
的频率很高,Writer角色便会频繁停止Reader角色的处理,这样就无法体现出Read-Write Lock模式的优点了。
6.6 java.util.concurrent.locks 包和 Read-Write Lock模式
6.6.1 java.util.concurrent.locks包
- 可用 ReadwriteLock 接口和 ReentrantReadwriteLock 类实现Read-Write Lock模式。
- ReentrantReadwriteLock类的主要特征如下:
-
公平性:当创建ReentrantReadWriteLock类的实例时,我们可以选择锁的获取顺序是否要设为公平(fair)的。如果创建的实例是公平的,那么等待时间久的线程将可以优先获取锁。
-
可重入性:ReentrantReadWriteLock类的锁是可重入的(reentrant)。也就是说,Reader角色的线程可以获取“用于写入的锁”,Writer角色的线程也可以获取“用于读取的锁”。
-
锁降级:ReentrantReadWriteLock类可以按:
获取用于写入的锁->获取用于读取的锁->释放用于写入的锁
对锁进行降级。但是,“用于读取的锁” 不可以升级为 “用于写入的锁”。