php二次网站开发步骤,wordpress顶部栏如何修改,html5手机论坛网站模板,网站建设对电子商务的作用在之前的文章中#xff0c;我们介绍了读写锁#xff0c;学习完之后你应该已经知道了读写锁允许多个线程同时访问共享变量#xff0c;适用于读多写少的场景。那么在读多写少的场景中还有没有更快的技术方案呢#xff1f;还真有#xff0c;在Java1.8这个版本里提供了一种叫S…在之前的文章中我们介绍了读写锁学习完之后你应该已经知道了读写锁允许多个线程同时访问共享变量适用于读多写少的场景。那么在读多写少的场景中还有没有更快的技术方案呢还真有在Java1.8这个版本里提供了一种叫StampedLock的锁它的性能就比读写锁还要好。 下面我们介就来介绍一下StampedLock的使用方法、内部工作原理以及在使用过程中需要注意的事项。
StampedLock支持的三种模式
我们先来看看StampedLock在使用什么和上篇文章中的ReadWriteLock所有哪些区别。 ReadWriteLock支持两种模式一种是读锁一种是写锁而StampedLock支持三种模式分别是写锁、悲观锁锁、乐观读其中写锁、悲观读锁的语义和ReadWriteLock的写锁、读锁的语义非常类似。允许多个线程同时获取悲观读锁但是只允许一个线程获取写锁写锁和悲观读锁都是互斥的然而不同的是里面的写锁和悲观读锁加锁成功之后都会返回一个stamp。然后解锁的时候需要传入这个stamp。相关的实例代码如下。
final StampedLock sl new StampedLock();//获取/释放悲观读锁示意代码
long stamp sl.reaLock();
try{//省略业务代码
} finally {sl.unlockRead(stamp);
}//获取/释放写锁示意代码
long stamp sl.writeLock();
try{//省略相关业务代码
} finally {sl.unlockWrite(stamp);
}StampedLock 的性能之所以比ReadWriteLock还要好关键是StampedLock支持乐观读的方式。ReadWriteLock支持多个线程同时读但是当多个线程同时读的时候所有的写操作也会被阻塞。而StampedLock提供的乐观读是允许一个线程获取写锁的也就是说不是所有写操作都被阻塞的。 注意这里我们用的是乐观读这个词而不是乐观读锁是要提醒你乐观读操作是无锁的所以相比较ReadWriteLock的读锁乐观读的性能更好一点。文中下面这段代码是出自于Java SDK官方示例并略作修改。在distanceFromRrigin()这个方法中首先通过调用tryOptimisticRead获得了一个stamp。这里的tryOptimisticRead就是我们前面提到的乐观读。之后将共享变量X和Y读入方法的局部变量中。不过需要注意的是由于tryOptimisticRead是无锁的所以共享变量X和Y读入方法局部变量时X和Y有可能被其他线程修改了因此最后读完之后还需要再次验证一下是否存在写操作这个操作是通过调用validate (stamp)来实现的。
class Point {private int x,y;final StampedLock sl new StampedLock();//计算到原点的距离int distanceFromOrigin(){//乐观读long stamp sl.tryOptimisticRead();//读入局部变量//读的过程中数据可能被修改int curX x,curY y;//判断执行读操作期间是否存在写操作//如果存在返回falseif(!sl.validate(stamp)){//升级为悲观锁stamp sl.readLock();try{curX x;curY y;} finally {sl.unLockRead(stamp);}}return Math.sqrt(curX * curX curY * curY);}
}在上面这个代码示例中如果执行乐观读操作期间存在写操作会把乐观读升级为悲观读锁。这个做法挺合理的否则你就需要在一个循环里反复执行乐观读直到执行乐观读操作期间没有写操作只有这样才能保证X和Y的正确性和一致性。而循环读会浪费大量的CPU。升级为悲观读锁代码简练且不易出错建议你在具体实践的时候也采用这样的方法。
进一步理解乐观读
如果你曾经用过数据库的乐观锁你可能会发现StampLock的乐观读和数据库的乐观读锁有异曲同工之妙。的确是这样的就我个人而言我是先接触数据库的乐观锁然后再接触的StampLock我就觉得我前期数据库里的乐观锁的学习对于后面的理解StampLock的乐观读有很大的帮助所以这里有必要再介绍一下数据库里的乐观锁。 还记得我第一次使用数据库乐观锁的场景是这样的在ERP的生产模块里会有多个人通过ERP系统提供的UI同时修改同一条生产订单那如何保证生产订单数据是并发安全的呢我采用的方案就是乐观锁。 乐观锁的实现很简单在生产订单的表product_doc里面增加一个数字型的版本号字段version每次更新product_doc这个表的时候都将version字段加1。生产订单的UI在展示的时候需要查询数据库。此时将这个version字段和其他业务字段一起返回给生产订单UI。假设用户查询的生产订单的ID777那么SQL语句类似于下面这样。
select id,....,version
from product_doc
where id 777用户在生产订单UI执行保存操作时候后台利用下面的SQL语句更新生产订单此时我们假设该条生产订单的version等于9。
update product_doc
set versionversion1
where id777 and versoin9如果这条语SQL语句成执行成功并且返回的条数等于1那么说明从生产订单UI执行查询操作到执行保存操作期间没有其他人修改过这条数据。因为如果这期间其他人修改过这条数据那么版本号一定会大于9。 你会发现数据库里的乐观锁查询的时候需要把version字段查出来更新的时候要利用version字段做校验这个version字段就类似于StampLock里面的stamp这样对比着看你相信你会更容易理解StampLock里面乐观读的用法。
StampLock使用注意事项
对于读多写少的场景StampLock性能很好简单的应用场景基本上可以替代ReadWriteLock但是StampLock的功能仅仅是ReadWriteLock的子集在使用的时候还是有几个需地方需要注意一下。 StampLock在命名上并没有增加Reentrant想必你已经猜到了StampLock应该是不可重入的事实上的确是这样的StampLock不支持重入这个是在使用中必须要注意的。 另外StampLock的悲观读锁、写锁都不支持条件变量这个你也需要注意。 还有一点需要特别注意那就是如果线程阻塞在StampLock的readLock()上时此时调用该阻塞线程的interrupt()方法会导致CPU飙升。 所以使用StampLock一定不要调用中断操作如果需要支持中断功能一定使用可中断的悲观读锁readLockInterruptibly()和写锁writeLockInterruptibly()这个规则一定要记清楚。
总结
StampLock的使用看上去有点复杂但是如果你能理解乐观所背后的原理使用起来还是比较流畅的。