Java多线程之读写锁 ReadWriteLock 使用
什么是读写锁 ReadWriteLock?
简单说就是写操作加入锁,读操作也加入锁。写锁也可以称之为独占锁,读锁也可以称之为共享锁。这里我们先不过多描述,直接演示代码看效果,然后总结。
ReadWriteLock 维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有 writer,读取锁可以由多个 reader 线程同时保持。写入锁是独占的。 所有 ReadWriteLock 实现都必须保证 writeLock 操作的内存同步效果也要保持与相关 readLock 的联系。也就是说,成功获取读锁的线程会看到写入锁之前版本所做的所有更新。 与互斥锁相比,读-写锁允许对共享数据进行更高级别的并发访问。虽然一次只有一个线程(writer 线程)可以修改共享数据,但在许多情况下,任何数量的线程可以同时读取共享数据(reader 线程),读-写锁利用了这一点。从理论上讲,与互斥锁相比,使用读-写锁所允许的并发性增强将带来更大的性能提高。在实践中,只有在多处理器上并且只在访问模式适用于共享数据时,才能完全实现并发性增强。 与互斥锁相比,使用读-写锁能否提升性能则取决于读写操作期间读取数据相对于修改数据的频率,以及数据的争用——即在同一时间试图对该数据执行读取或写入操作的线程数。例如,某个最初用数据填充并且之后不经常对其进行修改的 collection,因为经常对其进行搜索(比如搜索某种目录),所以这样的 collection 是使用读-写锁的理想候选者。但是,如果数据更新变得频繁,数据在大部分时间都被独占锁,这时,就算存在并发性增强,也是微不足道的。更进一步地说,如果读取操作所用时间太短,则读-写锁实现(它本身就比互斥锁复杂)的开销将成为主要的执行成本,在许多读-写锁实现仍然通过一小段代码将所有线程序列化时更是如此。最终,只有通过分析和测量,才能确定应用程序是否适合使用读-写锁。
代码演示
运行结果如下:
总结:从运行结果我们可以看出,使用传统的Lock锁的时候,在我们读写操作中,每次只能允许一个线程获取到这把锁才能进行读写操作,别的线程想要获取锁呢,就必须等待着持有锁的线程把锁给我释放喽。但是现实中,一般读操作都是很多人并发操作的,谁会等着你,所以这里使用传统的Lock锁加锁保证数据安全性,效率有点低,进而衍生出了共享锁诞生,既能保证数据安全性,又能提高读操作的吞吐量。
ReadWriteLock 使用
Java 中也为我们提供了共享锁的实现,下面先演示代码在总结。
运行结果如下:
我们来分析运行结果:第18行代码睡眠3s,也就是3s后启动写作1、2线程,先执行读者1、2线程,然后在看到第34行代码,是让读者线程睡眠5s,也就是只要有一个读者线程进来了,他可以持有这把锁5s中,5s过后才会释放。
总结一句话:读读共享,读写互斥
