实现原理
StampedLock 是 Java 8 引入的一种新的锁机制,旨在读多写少的场景下提供更高的并发性能。其核心在于使用一个 64 位的 long 类型变量来维护锁的状态,该状态不仅表示锁是否被持有,还包含了一个版本号(stamp),用于支持乐观读锁。
![图片[1]_精通Java并发:揭秘StampedLock的高效应用与实战技巧_知途无界](https://zhituwujie.com/wp-content/uploads/2025/01/d2b5ca33bd20250118131342.png)
- 锁状态:
- StampedLock 使用一个名为
state
的 long 类型变量来存储锁的状态。 - 这个状态包含了锁的类型(读锁、写锁)和版本号等信息。
- StampedLock 使用一个名为
- 乐观读锁:
- 当线程尝试获取乐观读锁时,StampedLock 会检查当前是否有写锁被持有。
- 如果没有写锁被持有,它会返回一个 stamp(当前状态的一个快照),并允许读操作继续,而不会阻塞其他读线程或写线程。
- 但需要注意的是,乐观读锁并不能保证数据的一致性。因此,在读取数据后,需要使用
validate(stamp)
方法来验证数据在读取期间是否被修改过。
- 悲观读锁:
- 与乐观读锁不同,悲观读锁会阻塞其他写线程的访问,以保证数据的一致性。
- 当线程尝试获取悲观读锁时,StampedLock 会检查是否有其他写线程持有锁或正在等待锁。
- 如果没有,它会授予锁并返回一个 stamp。
- 写锁:
- 写锁是独占的,意味着同一时间只能有一个线程持有写锁。
- 当线程尝试获取写锁时,StampedLock 会检查是否有其他读锁或写锁被持有。
- 如果有,线程将被阻塞直到锁被释放。
- 可重入性与锁转换:
- StampedLock 支持锁的可重入性,即一个线程可以多次获得同一个锁而不会导致死锁。
- StampedLock 还允许线程将乐观读锁转换为悲观读锁或写锁,或将悲观读锁转换为写锁,前提是在转换过程中没有其他线程获得相应的锁。
最佳实践
- 合理使用锁模式:
- 在写操作时,应使用写锁。
- 在读操作时,优先使用乐观读锁以提高并发性能。如果数据一致性要求非常高,或者乐观读锁验证失败,则可以使用悲观读锁。
- 实现超时机制:
- 在获取锁时,可以使用带超时的方法(如
tryWriteLock(long timeout, TimeUnit unit)
),以避免线程长时间等待锁而导致性能问题。
- 在获取锁时,可以使用带超时的方法(如
- 避免死锁:
- 由于 StampedLock 不支持重入锁的可重入性(注:这里存在争议,因为有些资料指出 StampedLock 支持可重入性,但使用时仍需谨慎),因此应避免在同一个线程中多次获取同一个锁。
- 如果需要在同一个线程中多次获取锁,应考虑使用其他锁机制(如 ReentrantLock)。
- 中断处理:
- 在使用可中断的锁获取方法时(如
writeLockInterruptibly()
),应正确处理中断异常,以避免线程中断后导致资源泄露或不一致状态。
- 在使用可中断的锁获取方法时(如
- 性能考虑:
- StampedLock 在读多写少的场景下性能优异,因为它减少了线程切换和锁竞争。
- 但如果写操作非常频繁,或者数据一致性要求非常高,那么可能需要考虑使用其他的锁机制(如 ReentrantLock 或 ReentrantReadWriteLock)。
- 代码示例:
import java.util.concurrent.locks.StampedLock;import java.util.concurrent.TimeUnit;public class StampedLockExample {private final StampedLock stampedLock = new StampedLock();private int sharedData = 0;// 使用乐观读锁读取共享数据public int getSharedDataWithOptimisticReadLock() {long stamp = stampedLock.tryOptimisticRead();int currentData = sharedData;if (!stampedLock.validate(stamp)) {// 乐观读锁验证失败,使用悲观读锁重新读取stamp = stampedLock.readLock();try {currentData = sharedData;} finally {stampedLock.unlockRead(stamp);}}return currentData;}// 使用写锁更新共享数据public void updateSharedDataWithWriteLock(int newData) {long writeStamp = stampedLock.writeLock();try {sharedData = newData;} finally {stampedLock.unlockWrite(writeStamp);}}// 使用带超时的写锁获取方法public boolean tryUpdateSharedDataWithWriteLock(int newData, long timeout, TimeUnit unit) {long stamp = stampedLock.tryWriteLock(timeout, unit);if (stamp == 0) {return false; // 获取锁超时}try {sharedData = newData;return true;} finally {stampedLock.unlockWrite(stamp);}}}import java.util.concurrent.locks.StampedLock; import java.util.concurrent.TimeUnit; public class StampedLockExample { private final StampedLock stampedLock = new StampedLock(); private int sharedData = 0; // 使用乐观读锁读取共享数据 public int getSharedDataWithOptimisticReadLock() { long stamp = stampedLock.tryOptimisticRead(); int currentData = sharedData; if (!stampedLock.validate(stamp)) { // 乐观读锁验证失败,使用悲观读锁重新读取 stamp = stampedLock.readLock(); try { currentData = sharedData; } finally { stampedLock.unlockRead(stamp); } } return currentData; } // 使用写锁更新共享数据 public void updateSharedDataWithWriteLock(int newData) { long writeStamp = stampedLock.writeLock(); try { sharedData = newData; } finally { stampedLock.unlockWrite(writeStamp); } } // 使用带超时的写锁获取方法 public boolean tryUpdateSharedDataWithWriteLock(int newData, long timeout, TimeUnit unit) { long stamp = stampedLock.tryWriteLock(timeout, unit); if (stamp == 0) { return false; // 获取锁超时 } try { sharedData = newData; return true; } finally { stampedLock.unlockWrite(stamp); } } }import java.util.concurrent.locks.StampedLock; import java.util.concurrent.TimeUnit; public class StampedLockExample { private final StampedLock stampedLock = new StampedLock(); private int sharedData = 0; // 使用乐观读锁读取共享数据 public int getSharedDataWithOptimisticReadLock() { long stamp = stampedLock.tryOptimisticRead(); int currentData = sharedData; if (!stampedLock.validate(stamp)) { // 乐观读锁验证失败,使用悲观读锁重新读取 stamp = stampedLock.readLock(); try { currentData = sharedData; } finally { stampedLock.unlockRead(stamp); } } return currentData; } // 使用写锁更新共享数据 public void updateSharedDataWithWriteLock(int newData) { long writeStamp = stampedLock.writeLock(); try { sharedData = newData; } finally { stampedLock.unlockWrite(writeStamp); } } // 使用带超时的写锁获取方法 public boolean tryUpdateSharedDataWithWriteLock(int newData, long timeout, TimeUnit unit) { long stamp = stampedLock.tryWriteLock(timeout, unit); if (stamp == 0) { return false; // 获取锁超时 } try { sharedData = newData; return true; } finally { stampedLock.unlockWrite(stamp); } } }
以上代码示例展示了如何使用 StampedLock 的乐观读锁、悲观读锁和写锁,以及如何实现带超时的写锁获取方法。
© 版权声明
文中内容均来源于公开资料,受限于信息的时效性和复杂性,可能存在误差或遗漏。我们已尽力确保内容的准确性,但对于因信息变更或错误导致的任何后果,本站不承担任何责任。如需引用本文内容,请注明出处并尊重原作者的版权。
THE END
暂无评论内容