面试官: 你对ConcurrentHashMap了解多少?
我回答:
ReadWriteLock
和StampedLock
都是Java并发库中提供的锁机制,它们各自针对不同场景提供了灵活性和性能优势。
ReadWriteLock
ReadWriteLock
是Java并发包中的一个接口,它提供了一种读写锁的实现,允许多个读操作同时进行,但写操作是独占的。这在读操作远多于写操作的场景下非常有用,因为它可以显著提高并发性能。
特点:
- 读-读共享:多个读线程可以同时持有读锁。
- 读-写互斥:读操作和写操作之间是互斥的,即当有写锁持有时,任何读锁都不能获得;反之亦然。
- 写-写互斥:写操作之间也是互斥的,即一次只能有一个写操作进行。
- 性能优化:在读多写少的场景下,相比独占锁,读写锁能显著提高程序的并发性能。
- 公平性:
ReentrantReadWriteLock
(ReadWriteLock
的一个常见实现)支持公平锁和非公平锁两种模式。公平锁按照线程请求锁的顺序来分配锁,而非公平锁则不保证这种顺序。
实现:
ReadWriteLock
接口有两个主要实现:ReentrantReadWriteLock
和StampedLock
(后者其实更通用,稍后详细介绍)。ReentrantReadWriteLock
是最常用的实现,它支持重入,即一个已经持有写锁的线程还可以再次获得写锁,而不会被阻塞。
使用场景:
- 数据库查询缓存:读操作远多于写操作,可以显著提高性能。
- 配置管理:读取配置信息的频率远高于更改配置的频率。
- 数据库连接池:获取连接的读操作远多于释放连接的写操作。
StampedLock
StampedLock
是一个更高级的锁机制,它提供了比ReadWriteLock
更丰富的功能,包括读锁、写锁以及乐观读锁(Optimistic Read Lock)。
特点:
- 读-读共享:与
ReadWriteLock
类似,多个读线程可以同时持有读锁。 - 读-写互斥:读操作和写操作之间是互斥的。
- 写-写互斥:写操作之间也是互斥的。
- 乐观读锁:
StampedLock
还提供了一种乐观读锁,允许读操作在没有显式获得读锁的情况下进行,但需要在读取结束后验证读取期间数据是否被修改。如果数据被修改,则需要重试读取。 - 悲观读和写锁:除了乐观读模式外,
StampedLock
还支持传统的悲观读锁(readLock
)和写锁(writeLock
)。 - 不可重入:与
ReentrantReadWriteLock
不同,StampedLock
不是可重入的。
实现:
StampedLock
使用一个长整型的“戳记”(Stamp)来标记锁的状态,这个戳记可以用来表示读锁、写锁或者乐观读锁。线程在获得锁时会得到一个戳记,释放锁时会验证这个戳记是否仍然有效。
使用场景:
- 需要读操作性能最大化的场景。
- 需要支持乐观读锁的场景,即在读取数据时不立即加锁,而是尝试读取,如果检测到数据在读取期间被修改,则重试读取。
总结
ReadWriteLock
和StampedLock
都是Java中用于实现读写分离的高级锁机制。ReadWriteLock
通过读写分离提高了读操作的并发性,适用于读多写少的场景。而StampedLock
在ReadWriteLock
的基础上引入了乐观读模式,进一步提高了读操作的性能,特别是在读操作远多于写操作的场景中。然而,需要注意的是,StampedLock
的使用需要更加谨慎,因为它不提供可重入性和公平性保证。在选择使用哪种锁机制时,需要根据具体的并发需求和场景综合考虑。