当前位置: 首页 > 科技观察

探索ava的新特性8-StampedLock将是解决同步问题的新宠

时间:2023-03-21 23:44:10 科技观察

synchronized在java5之前,同步主要是使用synchronized实现的。它是Java语言中的关键字。当用于修改方法或代码块时,可以保证最多有一个线程同时执行代码。同步块有四种不同类型:InstancemethodsSynchronizationblocksinstaticmethodsInstancemethodsSynchronizationblocksinstaticmethods大家应该不陌生,我就不说了,下面是代码示例?123synchronized(this)//dooperation}总结:synchronized一直是多线程并发编程的老手。很多人会称之为重量级锁。不过在JavaSE1.6中对Synchronized进行了各种优化后,性能也有所提升。Lock是Java5中java.util.concurrent.locks中新增的API。Lock是一个接口,核心方法有lock()、unlock()、tryLock(),实现类有ReentrantLock、ReentrantReadWriteLock.ReadLock、ReentrantReadWriteLock.写锁;ReentrantReadWriteLock、ReentrantLock和同步锁都具有相同的内存语义。与synchronized不同的是,Lock是完全用Java编写的,与java层面的JVM实现无关。Lock提供了更灵活的锁定机制。很多synchronized没有提供的特性,比如锁投票,定时锁等待,中断锁等待,但是因为lock是代码实现的,要保证锁会被释放,必须把unLock()设置成finally{}下面是Lock123456rwlock.writeLock().lock();的代码示例try{//dooperation}finally{rwlock.writeLock().unlock();}总结:一种比synchronized更加灵活和可扩展的锁机制,但无论如何,synchronized的代码更容易编写StampedLock,它是java8中java.util.concurrent.locks中的一个新API。ReentrantReadWriteLock只有在没有读写锁的情况下才能获得写锁,可以用来实现悲观读(PessimisticReading),即如果在执行过程中进行读操作,可能经常会有另外一次执行需要写操作,为了保持同步,ReentrantReadWriteLock的读锁就可以派上用场了。但是,如果读执行多而写少,使用ReentrantReadWriteLock可能会导致写线程饿死(Starvation),即写线程无法竞争锁,一直处于等待状态。StampedLock控制锁有三种模式(写、读、乐观读),一个StampedLock状态由版本和模式组成,锁获取方法返回一个数字作为票戳,由相应的锁状态表示并控制访问,数字0表示没有为访问授予写锁。读锁分为悲观锁和乐观锁。所谓乐观读模式,即如果读操作多而写操作少,可以乐观地认为同时写读的机会很小,所以不悲观地使用fullread加锁,程序可以在读取数据后通过写入和执行来检查是否被更改,然后采取后续措施(重新读取更改信息,或者抛出异常)。这个小小的改进可以大大提高程序的吞吐量!!这是javadoc提供的StampedLock的例子?41424344454647484950类点{私人双工,y;privatefinalStampedLocksl=newStampedLock();voidmove(doubledeltaX,doubledeltaY){//独占锁定方法longstamp=sl.writeLock();试试{x+=deltaX;y+=增量Y;}最后{sl.unlockWrite(邮票);//再看乐观读锁案例doubledistanceFromOrigin(){//Aread-onlymethodlongstamp=sl.tryOptimisticRead();//获取乐观读锁doublecurrentX=x,currentY=y;//将两个字段读入本地局部变量if(!sl.validate(stamp)){//检查乐观读锁发出后是否同时有其他写锁?stamp=sl.readLock();//如果没有,我们再次获取读悲观锁{sl.unlockRead(邮票);}}returnMath.sqrt(currentX*currentX+currentY*currentY);}//下面是一个悲观的读锁casevoidmoveIfAtOrigin(doublenewX,doublenewY){//upgrade//Couldinsteadstartwithoptimistic,notreadmodelongstamp=sl.readLock();try{while(x==0.0&&y==0.0){//循环,检查当前状态是否一致longws=sl.tryConvertToWriteLock(stamp);//将读Lock转为写锁if(ws!=0L){//这里是确认是否转换成写锁成功stamp=ws;//如果换票成功x=newX;//改变状态y=newY;//进行状态改变break;}else{//如果不能成功转换为写锁sl.unlockRead(stamp);//我们显式释放读锁stamp=sl.writeLock();//直接显式写锁,然后通过重试在一个循环中}}}finally{sl.unlock(stamp);//释放读锁或写锁}}}总结:StampedLock比ReentrantReadWriteLock便宜,即消耗比较小。StampedLock和ReadWriteLock的性能比较如下图所示。相比之下,在一个线程的情况下,读取速度大约快4倍,写入速度快1倍。下图是在6线程的情况下,读性能提升几十倍,写性能提升近10倍:下图是吞吐量的提升:总结1.synchronized是在JVM层面实现的,不仅通过一些监控工具监控synchronized锁,当代码执行过程中出现异常时,JVM会自动释放锁;2、ReentrantLock、ReentrantReadWriteLock、StampedLock都是对象级锁。为保证锁会被释放,必须把unLock()放在finally{};3、StampedLock在吞吐量上有巨大的提升,尤其是在读线程越来越多的场景下;4.StampedLockAPI复杂,容易误用其他方法进行加锁操作;5、当只有少数竞争者时,synchronized是一个很好的通用锁实现;6、当线程增长可以预估时,ReentrantLock是一个很好的通用锁实现;