1.自旋锁和自适应自旋锁

sync在JDK1.6之前之所以被称为重量级锁,是因为对于互斥同步的性能来说,影响最大的就是阻塞的实现。挂起线程与恢复线程的操作都需要转入内核态中完成。从用户态转入内核态是比较耗费系统性能的。

研究表明,大多数情况下,线程持有锁的时间都不会太长,如果直接挂起OS层面的线程可能会得不偿失,毕竟OS实现线程之间的切换需要从用户态转换为核心态。这个转换时间成本相对较高。自旋锁会假设在不久将来,当前的线程可以获得锁,因此虚拟机会让当前想要获取锁的线程做几个空循环,使当前线程不放弃处理器的执行时间(这也是称为自旋的原因),在经过若干循环后,如果得到锁,就顺利进入临界区。

但是阿, 自旋不能替代阻塞。首先,自旋需要多处理器或者一个处理器多个核心的CPU环境。这样才能保证两个及以上的线程并行执行(一个是获取锁的执行线程,一个是进行自旋的线程)除了对处理器数量有要求外,自旋虽然避免了线程切换的开销,但他是要占用处理器时间的,因此,如果锁被占用的时间短,自旋的效果才好,否则只是白白占用了CPU资源,带来性能的浪费。

那么自旋需要有一定的限度,如果自旋超过了一定的次数之后,还没有成功获取锁,就只能进行挂起了,这个次数默认是10.

在JDK1.4.2 引入了自旋锁,在JDK1.6引入了适应性自旋锁。自适应意味着自旋的时间不再固定。

适应性自旋锁:

如果同一个锁对象上,自旋等待刚刚成功获取锁,并且持有锁的线程正在运行,那么虚拟机就会认为此次自旋也很有可能成功,进而它将允许自旋等待持续相对更长的时间,比如100个循环。如果对于某个锁,自旋很少成功获取过,那么在以后获取这个锁时将可能自动省略掉自旋过程,以免浪费处理器资源。有了自适应自旋,随着程序运行和性能监控不断完善,虚拟机对程序锁的状况预测就会越来越精准,虚拟机也会越来越聪明。


锁消除

消除锁是更加彻底的锁优化。JVM在JIT编译时通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁,通过这种方式消除没必要的锁。

锁消除的主要判断依据是逃逸分析技术的支持。

也许你疑惑,变量是否逃逸,程序员本身应该可以判断,怎么会存在明明知道不存在数据竞争的情况下还是用同步?

public String concatString(String s1, String s2, String s3) {
return s1 + s2 + s3;
}

由于String是一个不可变类,因此字符串的连接操作总是通过新生成的String对象来进行。在JDK1.5之前,javac编译器会对String连接进行自动优化,将连接转换为StringBuffer对象的连续append操作,在JDK1.5之后,会转化为StringBuilder对象的append操作,也就是说,上述代码经过javac优化之后,会变成下面这样:

public String concatString(String s1, String s2, String s3) {
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
sb.append(s3); return sb.toString();
}

StringBuffer是一个线程安全的类,在它的append方法中有一个同步块,锁对象就是sb,但是JVM观察变量sb,发现他是一个局部变量,本身线程安全,并不需要额外的同步机制。因此,这里虽有锁,但可以被安全的清除,在JIT编译之后,这段代码就会忽略所有的同步而执行。这就是锁清除。


锁粗化

原则上,我们在使用同步块,总是建议将同步快的作用范围限制的尽量小——使需要同步的操作数量尽可能变小,在存在锁竞争的情况下,等待锁的线程可以尽快的拿到锁。

大部分情况下,上述原则都正确,但是存在特殊情况下,如果一系列下来,都对同一个对象反复加锁、解锁,甚至加锁与解锁操作出现在循环体,那即使没有线程竞争,频繁的进行互斥同步操作也会导致不必要的性能损耗。

如上述代码中的append方法。如果虚拟机探测到了这样的操作,就会把加锁的同步范围扩展(粗化)到整个操作序列的外部。

以上述代码为例,就是拓展到第一个append操作之前直至最后一个append操作之后,这样只需要加锁一次。


偏向锁会偏向第一个获取它的线程,如果在接下来的执行过程中,该锁没有被其他线程获取,则持有偏向锁的线程将永远不需要进行同步。

HotSpot作者经过以往的研究发现大多数情况下锁不仅不存在多线程竞争,而且总是由同一线程多次获得(比如在单线程中使用StringBuffer类),为了让线程获得锁的代价更低而引入了偏向锁。当锁对象第一次被线程获取的时候,虚拟机把对象头中的标志位设为“01”,即偏向模式。同时使用CAS操作把获取这个锁的线程ID记录在对象的Mark Word中,如果CAS操作成功,持有偏向锁的线程以后每次进入这个锁相关的同步块时,虚拟机都可以不用进行任何同步操作。

当有另一个线程去尝试获取这个锁时,偏向模式就宣告结束。


synchronized锁优化的更多相关文章

  1. 老掉牙的 synchronized 锁优化,一次给你讲清楚!

    我们都知道 synchronized 关键字能实现线程安全,但是你知道这背后的原理是什么吗?今天我们就来讲一讲 synchronized 实现线程同步背后的原因,以及相关的锁优化策略吧. synchr ...

  2. synchronized 锁优化

    synchronized 在jdk 1.7之前是重量级锁,独占锁,非公平锁.jdk1.7之后,synchronized引入了 偏向锁,自旋锁,轻量级锁,重量级锁 自旋锁 当线程在获取锁的时候,如果发现 ...

  3. synchronized锁详解

    synchronized的意义 解决了Java共享内存模型带来的线程安全问题: 如:两个线程对初始值为 0 的静态变量一个做自增,一个做自减,各做 5000 次,结果是 0 吗?(针对这个问题进行分析 ...

  4. Java并发编程:synchronized和锁优化

    1. 使用方法 synchronized 是 java 中最常用的保证线程安全的方式,synchronized 的作用主要有三方面: 确保线程互斥的访问代码块,同一时刻只有一个方法可以进入到临界区 保 ...

  5. Synchronized锁性能优化偏向锁轻量级锁升级 多线程中篇(五)

    不止一次的提到过,synchronized是Java内置的机制,是JVM层面的,而Lock则是接口,是JDK层面的 尽管最初synchronized的性能效率比较差,但是随着版本的升级,synchro ...

  6. Java并发编程:Synchronized底层优化(偏向锁、轻量级锁)

    Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...

  7. 015-线程同步-synchronized几种加锁方式、Java对象头和Monitor、Mutex Lock、JDK1.6对synchronized锁的优化实现

    一.synchronized概述基本使用 为确保共享变量不会出现并发问题,通常会对修改共享变量的代码块用synchronized加锁,确保同一时刻只有一个线程在修改共享变量,从而避免并发问题. syn ...

  8. 并发-Synchronized底层优化(偏向锁、轻量级锁)

    Synchronized底层优化(偏向锁.轻量级锁) 参考: http://www.cnblogs.com/paddix/p/5405678.html 一.重量级锁 上篇文章中向大家介绍了Synchr ...

  9. 【转】Java并发编程:Synchronized底层优化(偏向锁、轻量级锁)

     一.重量级锁 上篇文章中向大家介绍了Synchronized的用法及其实现的原理.现在我们应该知道,Synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的.但是监视器锁本 ...

随机推荐

  1. crontab定时任务_net

    2017年2月25日, 星期六 crontab定时任务 19. crontab 定时任务 通过crontab 命令,我们可以在固定的间隔时间执行指定的系统指令或 shell script脚本.时间间隔 ...

  2. spark科普

    普Spark,Spark是什么,如何使用Spark(1)转自:http://www.aboutyun.com/thread-6849-1-1.html 阅读本文章可以带着下面问题:1.Spark基于什 ...

  3. Spring Enable*高级应用及原理

    Enable* 之前的文章用到了一些Enable*开头的注解,比如EnableAsync.EnableScheduling.EnableAspectJAutoProxy.EnableCaching等, ...

  4. 【CodeForces】600 E. Lomsat gelral (dsu on tree)

    [题目]E. Lomsat gelral [题意]给定n个点的树,1为根,每个点有一种颜色ci,一种颜色占领一棵子树当且仅当子树内没有颜色的出现次数超过它,求n个答案——每棵子树的占领颜色的编号和Σc ...

  5. Anaconda+django写出第一个web app(二)

    今天开始建立App中的第一个Model,命名为Tutorial. Model的定义在main文件夹下的models.py中通过类进行,我们希望Tutorial这个model包含三个属性:标题.内容和发 ...

  6. HDU 2722 Here We Go(relians) Again (最短路)

    题目链接 Problem Description The Gorelians are a warlike race that travel the universe conquering new wo ...

  7. CSS基础之选择器

    一:CSS介绍 CSS(Cascading Style Sheet,层叠样式表)定义如何显示HTML元素. 当浏览器读到,一个样式表时,就会按照. 二:CSS语法 每个CSS有两部分组成:选择器和声明 ...

  8. 在window 8 或windows2012 上用命令行安装framework3.5 方法

    找到对应操作系统安装目录的sources文件夹下的sxs文件夹,拷贝到本地电脑,如F:盘 根目录下 CMD(管理员身份)命令: dism.exe /online /enable-feature /fe ...

  9. 【译】第五篇 Integration Services:增量加载-Deleting Rows

    本篇文章是Integration Services系列的第五篇,详细内容请参考原文. 在上一篇你学习了如何将更新从源传送到目标.你同样学习了使用基于集合的更新优化这项功能.回顾增量加载记住,在SSIS ...

  10. 如何清理休眠文件(hiberfil.sys)

    如果使用了休眠功能,那么打开系统盘就会有一个很大(5.36G)的hiberfil.sys文件,它是将用户正在运行的程序,保存在这里,再启动系统就很快了.如要清理它(不用休眠功能,或者临时腾出空间),可 ...