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. React基础笔记

    参考文章: http://www.ruanyifeng.com/blog/2015/03/react.html https://segmentfault.com/a/1190000002767365 ...

  2. iScroll使用方法

    最基本的iScroll使用结构 <div id="wrapper"> <ul> <li></li> ..... </ul> ...

  3. Python json转字符串的一些细节

    要调PHP那边的接口,php那边一直都校验不过,很是郁闷.没办法,只能让人把发送成功的代码拿过来看,不过是php写的,步骤都是一样: php端: 1. json对象转json字符串. 2. 对json ...

  4. Asp.net 中,在服务端向客户端写脚本的常用方法

    在Asp.net 服务端处理脚本,一般都用 ClientScriptManager ,即web窗体服务端的this.ClientScript.该对象比较常用的方法: 1.RegisterArrayDe ...

  5. 【leetcode 简单】 第八十一题 4的幂

    给定一个整数 (32 位有符号整数),请编写一个函数来判断它是否是 4 的幂次方. 示例 1: 输入: 16 输出: true 示例 2: 输入: 5 输出: false 进阶: 你能不使用循环或者递 ...

  6. MySQL主从复制-指定数据库复制

    在/etc/my.cnf添加需要进行同步的数据库信息 #需要进行同步的数据库 #replicate-do-db=custmgr #replicate-do-db=sdata #replicate-ig ...

  7. Linux禁止ping的俩种方法【转】

    Linux禁止ping以及开启ping的方法   Linux默认是允许Ping响应的,系统是否允许Ping由2个因素决定的:A.内核参数,B.防火墙,需要2个因素同时允许才能允许Ping,2个因素有任 ...

  8. SpringCloud常用注解

    一 @EnableDiscoveryClient,@EnableEurekaClient的区别 SpringCLoud中的“Discovery Service”有多种实现,比如:eureka, con ...

  9. js函数前加分号和感叹号是什么意思?有什么用?

    一般看JQuery插件里的写法是这样的 (function($) { //... })(jQuery); 今天看到bootstrap的javascript组件是这样写的 !function( $ ){ ...

  10. jenkins主从服务器部署

    当服务器为linux系统但也有部分ios代码,此时就需要添加一个从jenkins以便编译ios代码.或者需要多个job同时编译这时就需要搭建主从服务器. 1.主(master)节点安装jenkins ...