网站制作价格低,中国摄影网站,南京网站开发南京乐识好,网站 伪静态在现代的Java应用中#xff0c;同步是一个核心问题#xff0c;尤其是在高并发环境下。Java提供了多种同步机制#xff0c;从基本的synchronized关键字到更高级的ReentrantLock。但在Java 8中#xff0c;引入了一个新的同步原语——StampedLock#xff0c;它旨在提供更高的…在现代的Java应用中同步是一个核心问题尤其是在高并发环境下。Java提供了多种同步机制从基本的synchronized关键字到更高级的ReentrantLock。但在Java 8中引入了一个新的同步原语——StampedLock它旨在提供更高的性能特别是在读操作远多于写操作的场景中。
1.什么是StampedLock
StampedLock是一个同步工具它支持三种访问模式写锁、乐观读和悲观读。这三种模式使得StampedLock能够在不同的使用场景下提供更高的吞吐量。 StampedLock 是 Java 8 引入的一种新的同步原语用于替代 ReentrantLock 以提供更高的并发性能。它使用了一种称为 “乐观读”optimistic reading的技术以及 “写锁”write lock和 “读锁”read lock的分离以优化读多写少的场景。
2.特点
三种访问模式
写锁独占锁用于修改数据。乐观读不阻塞其他读或写但在数据实际被读取前会检查锁是否已被其他线程获取。悲观读阻塞写但不阻塞其他读。
优化读操作在大量读操作和较少写操作的场景中StampedLock 可以提供更好的性能。不可重入与 ReentrantLock 不同StampedLock 不是可重入的。无条件公平性StampedLock 不提供任何公平性保证。
3.构造函数和相关方法
实例化
StampedLock lock new StampedLock();写锁
long stamp lock.writeLock();
try {// 修改共享数据的代码
} finally {lock.unlockWrite(stamp);
}乐观读
long stamp lock.tryOptimisticRead();
// 读取共享数据的代码
if (!lock.validate(stamp)) {// 如果在读取过程中锁被其他线程获取则执行以下代码stamp lock.readLock();try {// 重新读取共享数据的代码} finally {lock.unlockRead(stamp);}
}悲观读
long stamp lock.readLock();
try {// 读取共享数据的代码
} finally {lock.unlockRead(stamp);
}注意事项
由于 StampedLock 不可重入因此在同一个线程中多次获取同一个锁时必须小心。StampedLock 没有与 Condition 类似的机制因此不适合需要等待/通知模式的场景。在使用乐观读时需要注意 validate() 方法的调用以确保在读取过程中锁没有被其他线程获取。
4.为什么选择StampedLock
与传统的ReentrantLock相比StampedLock在以下方面提供了优势
性能StampedLock通过乐观读和悲观读的分离优化了读多写少的场景。在大量读操作的场景下StampedLock可以提供比ReentrantLock更高的吞吐量。灵活性开发者可以根据具体的使用场景选择合适的锁模式。例如在数据更新不频繁但读取非常频繁的场景下乐观读可能是一个更好的选择。
5.使用示例
首先是Counter类它使用StampedLock来保护其内部计数器
import java.util.concurrent.locks.StampedLock;public class Counter {private int count;private final StampedLock lock new StampedLock();public void increment() {long stamp lock.writeLock();try {count;} finally {lock.unlockWrite(stamp);}}public int read() {long stamp lock.readLock();try {return count;} finally {lock.unlockRead(stamp);}}public int optimisticRead() {long stamp lock.tryOptimisticRead();int currentCount count;// 检查在读取过程中是否有写操作if (!lock.validate(stamp)) {// 如果写锁已被获取则升级为悲观读锁stamp lock.readLock();try {currentCount count;} finally {lock.unlockRead(stamp);}}return currentCount;}
}接下来是测试类CounterTest它将创建多个线程来模拟并发读写操作
public class CounterTest {public static void main(String[] args) throws InterruptedException {final Counter counter new Counter();// 创建并启动写线程Thread writer new Thread(() - {for (int i 0; i 1000; i) {counter.increment();}});// 创建并启动读线程Thread reader new Thread(() - {int sum 0;for (int i 0; i 1000; i) {sum counter.read();}System.out.println(Sum read via pessimistic lock: sum);});// 创建并启动乐观读线程Thread optimisticReader new Thread(() - {int optimisticSum 0;for (int i 0; i 1000; i) {optimisticSum counter.optimisticRead();}System.out.println(Sum read via optimistic lock: optimisticSum);});// 启动所有线程writer.start();reader.start();optimisticReader.start();// 等待所有线程完成writer.join();reader.join();optimisticReader.join();// 打印最终计数器的值System.out.println(Final counter value: counter.read());}
}运行结果
Sum read via pessimistic lock: 999000
Sum read via optimistic lock: 990000
Final counter value: 1000在这个例子中pessimistic lock悲观锁指的是使用readLock方法获取的读锁它保证在读取计数器时不会被写线程中断。而optimistic lock乐观锁则尝试在不阻塞的情况下读取计数器但如果在读取过程中发生了写操作则会重新读取。
由于乐观读不保证每次都能成功所以在高并发环境下乐观读计算的和可能会小于实际写入的次数。然而在读多写少且写冲突不频繁的场景下乐观读通常能够提供更高的吞吐量。
6.总结
StampedLock是一个强大的同步工具它在特定的使用场景下可以提供比传统锁更高的性能。