文档章节

Java 8新特性探究(十)StampedLock将是解决同步问题的新宠

沃德天拉莫帅
 沃德天拉莫帅
发布于 2014/05/13 20:33
字数 1613
阅读 7528
收藏 173

Java8就像一个宝藏,一个小的API改进,也足与写一篇文章,比如同步,一直是多线程并发编程的一个老话题,相信没有人喜欢同步的代码,这会降低应用的吞吐量等性能指标,最坏的时候会挂起死机,但是即使这样你也没得选择,因为要保证信息的正确性。所以本文决定将从synchronized、Lock到Java8新增的StampedLock进行对比分析,相信StampedLock不会让大家失望。

synchronized

在java5之前,实现同步主要是使用synchronized。它是Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。

有四种不同的同步块:

  1. 实例方法

  2. 静态方法

  3. 实例方法中的同步块

  4. 静态方法中的同步块

大家对此应该不陌生,所以不多讲了,以下是代码示例

synchronized(this)
// do operation
}

小结:在多线程并发编程中Synchronized一直是元老级角色,很多人都会称呼它为重量级锁,但是随着Java SE1.6对Synchronized进行了各种优化之后,性能上也有所提升。

Lock

它是Java 5在java.util.concurrent.locks新增的一个API。

Lock是一个接口,核心方法是lock(),unlock(),tryLock(),实现类有ReentrantLock, ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock;

ReentrantReadWriteLock, ReentrantLock 和synchronized锁都有相同的内存语义。

与synchronized不同的是,Lock完全用Java写成,在java这个层面是无关JVM实现的。Lock提供更灵活的锁机制,很多synchronized 没有提供的许多特性,比如锁投票,定时锁等候和中断锁等候,但因为lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中

下面是Lock的一个代码示例

rwlock.writeLock().lock();
try {
// do operation
} finally {
rwlock.writeLock().unlock();
}

小结:比synchronized更灵活、更具可伸缩性的锁定机制,但不管怎么说还是synchronized代码要更容易书写些

StampedLock

它是java8在java.util.concurrent.locks新增的一个API。

ReentrantReadWriteLock 在沒有任何读写锁时,才可以取得写入锁,这可用于实现了悲观读取(Pessimistic Reading),即如果执行中进行读取时,经常可能有另一执行要写入的需求,为了保持同步,ReentrantReadWriteLock 的读取锁定就可派上用场。

然而,如果读取执行情况很多,写入很少的情况下,使用 ReentrantReadWriteLock 可能会使写入线程遭遇饥饿(Starvation)问题,也就是写入线程吃吃无法竞争到锁定而一直处于等待状态。

StampedLock控制锁有三种模式(写,读,乐观读),一个StampedLock状态是由版本和模式两个部分组成,锁获取方法返回一个数字作为票据stamp,它用相应的锁状态表示并控制访问,数字0表示没有写锁被授权访问。在读锁上分为悲观锁和乐观锁。

所谓的乐观读模式,也就是若读的操作很多,写的操作很少的情况下,你可以乐观地认为,写入与读取同时发生几率很少,因此不悲观地使用完全的读取锁定,程序可以查看读取资料之后,是否遭到写入执行的变更,再采取后续的措施(重新读取变更信息,或者抛出异常) ,这一个小小改进,可大幅度提高程序的吞吐量!!

下面是java doc提供的StampedLock一个例子

class Point {
   private double x, y;
   private final StampedLock sl = new StampedLock();
   void move(double deltaX, double deltaY) { // an exclusively locked method
     long stamp = sl.writeLock();
     try {
       x += deltaX;
       y += deltaY;
     } finally {
       sl.unlockWrite(stamp);
     }
   }
  //下面看看乐观读锁案例
   double distanceFromOrigin() { // A read-only method
     long stamp = sl.tryOptimisticRead(); //获得一个乐观读锁
     double currentX = x, currentY = y; //将两个字段读入本地局部变量
     if (!sl.validate(stamp)) { //检查发出乐观读锁后同时是否有其他写锁发生?
        stamp = sl.readLock(); //如果没有,我们再次获得一个读悲观锁
        try {
          currentX = x; // 将两个字段读入本地局部变量
          currentY = y; // 将两个字段读入本地局部变量
        } finally {
           sl.unlockRead(stamp);
        }
     }
     return Math.sqrt(currentX * currentX + currentY * currentY);
   }
//下面是悲观读锁案例
   void moveIfAtOrigin(double newX, double newY) { // upgrade
     // Could instead start with optimistic, not read mode
     long stamp = sl.readLock();
     try {
       while (x == 0.0 && y == 0.0) { //循环,检查当前状态是否符合
         long ws = sl.tryConvertToWriteLock(stamp); //将读锁转为写锁
         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性能对比

下图是和ReadWritLock相比,在一个线程情况下,是读速度其4倍左右,写是1倍。

下图是六个线程情况下,读性能是其几十倍,写性能也是近10倍左右:

下图是吞吐量提高:

总结

1、synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定;

2、ReentrantLock、ReentrantReadWriteLock,、StampedLock都是对象层面的锁定,要保证锁定一定会被释放,就必须将unLock()放到finally{}中;

3、StampedLock 对吞吐量有巨大的改进,特别是在读线程越来越多的场景下;

4、StampedLock有一个复杂的API,对于加锁操作,很容易误用其他方法;

5、当只有少量竞争者的时候,synchronized是一个很好的通用的锁实现;

6、当线程增长能够预估,ReentrantLock是一个很好的通用的锁实现;

StampedLock 可以说是Lock的一个很好的补充,吞吐量以及性能上的提升足以打动很多人了,但并不是说要替代之前Lock的东西,毕竟他还是有些应用场景的,起码API比StampedLock容易入手,下篇博文争取更新快一点,可能会是Nashorn的内容,这里允许我先卖个关子。。。





© 著作权归作者所有

沃德天拉莫帅

沃德天拉莫帅

粉丝 3781
博文 22
码字总数 26172
作品 1
深圳
技术主管
私信 提问
加载中

评论(30)

k
keenlight
拜读了阁下大文,对代码17行,18行的注释感到疑惑,为何发出乐观读锁同时没有写锁发生还要去获取一个悲观读锁并重新读取一次值? 我查了API及做了实验了解到:
!sl.validate(stamp)这段代码的效果应该是若有写锁,则返回false,无写锁,则返回true。因为根据validate方法的描述可知,当参数stamp为0时,一律返回false。
而stamp若为0,也就意味着同时有写锁发生,因为根据api文档可知tryOptimisticRead方法仅当没有被写锁锁定时才会返回非零值,原文如下:
Method tryOptimisticRead() returns a non-zero stamp only if the lock is not currently held in write mode.
所以正文中所说的“数字0表示没有写锁被授权访问。”应该也是有误的。
有资本再款
有资本再款

引用来自“shawumus”的评论

29不错
2
有资本再款
有资本再款
good
有资本再款
有资本再款

引用来自“shawumus”的评论

29不错
wi_wi
wi_wi
stampedlockz新锁
wi_wi
wi_wi
stampedlockz新锁
empireghost
empireghost
这种性能图表都是怎么产生的???
沃德天拉莫帅
沃德天拉莫帅 博主

引用来自“empireghost”的评论

这种性能图表都是怎么产生的???
http://javaspecialists.eu/talks/jfokus13/PhaserAndStampedLock.pdf 估计是有测试程序,但在这里没找到
empireghost
empireghost
这种性能图表都是怎么产生的???
沃德天拉莫帅
沃德天拉莫帅 博主

引用来自“惉惉自喜”的评论

也是基于aqs?看来算法越来越复杂了
用起来方便就行了,其他都好说。。
你可能没听过的 Java 8 中的 10 个特性

当谈论起java8时,我们总是会听到那让人津津乐道的Lambda表达式,但这也只能算是java8的冰山一角。java8还有些从一开始鲜有人知的新特性——很多强大的类和语法糖。 在这里列举了10个我认为在...

oschina
2014/05/01
10.1K
35
Java 8新特性探究 前言

自2013年6月13日,oracle就已经发布的Java 8特性完备版本(M7),但最终GA版本将在2014年3月18日(已第二次跳票,原计划今年9月发布的,据官网宣称是为了解决安全问题......),相信大家多多...

OSC闲人
2013/11/05
4.7K
17
Java 8新增特性优缺点

Java 8于今年三月份正式发布了。那么它是否如我们之前所期待的那样呢?下面我们就一一查看Java8新增特性的优缺点吧。 Java 8试图“创新”,根据 微软对这个词的定义,就是把其他框架或语言里...

Emilypz
2015/09/25
827
1
Java魔法类——Unsafe应用解析

前言 Unsafe是位于sun.misc包下的一个类,主要提供一些用于执行低级别、不安全操作的方法,如直接访问系统内存资源、自主管理内存资源等,这些方法在提升Java运行效率、增强Java语言底层资源...

微笑向暖wx
02/19
20
0
Java 8新增特性优缺点

Java 8于今年三月份正式发布了。那么它是否如我们之前所期待的那样呢?下面我们就一一查看Java8新增特性的优缺点吧。 Java 8试图“创新”,根据 微软对这个词的定义,就是把其他框架或语言里...

Emilypz
2015/09/25
586
1

没有更多内容

加载失败,请刷新页面

加载更多

聊聊nacos的NacosDiscoveryAutoConfiguration

序 本文主要研究一下nacos的NacosDiscoveryAutoConfiguration NacosDiscoveryAutoConfiguration nacos-spring-boot-project/nacos-discovery-spring-boot-autoconfigure/src/main/java/com/a......

go4it
16分钟前
4
0
如何保证消息的顺序性?

面试题 如何保证消息的顺序性? 面试官心理分析 其实这个也是用 MQ 的时候必问的话题,第一看看你了不了解顺序这个事儿?第二看看你有没有办法保证消息是有顺序的?这是生产系统中常见的问题...

米兜
21分钟前
6
0
变量求解:RMT函数

1. RMT函数:计算贷款每月付款额 = PMT (贷款利率,付款限期,本金) 2.单变量求解: 数据选项卡----> 模拟分析------>单变量求解:单变量求解前必须先执行PMT函数...

东方墨天
22分钟前
2
2
网络安全市场需求

最近,网络安全技能差距的热门话题流传开来。技能差距经常被紧急讨论,可以看出它在实践中的作用是很大的。但信息安全是一门广泛的学科,所以在谈论“技能差距”时需要更具体。有专家表示,真...

linuxCool
42分钟前
3
0
饿了么快应用初体验

作者:饿了么 顾诚 为什么我们选择了快应用 在很长一段时间里,原生饿了么应用对于新用户来说体验成本略高,对于迫切想要点餐的老用户操作有点繁琐;而 Web 版的饿了么应用在体验、速度、功能...

前端老手
44分钟前
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部