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. nginx 跨域配置

    server { listen 80; server_name b.com; location /{ if ( $http_referer ~* (a.com|b.com|c.com) ) { Acc ...

  2. 在windows的IDEA运行Presto

    After building Presto for the first time, you can load the project into your IDE and run the server. ...

  3. PHP面向对象__set(赋值方法)

    //类Ren里面的变量都是私有的,子类$r里面直接取是取不到的,正常给变量赋值的方法为$r->__set("age",20);,但是可以直接写成$r->age = 20 ...

  4. 使用spring的监听器来完成系统超级管理员的注册

    1.注入 2“util类 package com.liveyc.mgrsite.util; import org.springframework.beans.factory.annotation.Au ...

  5. 【leetcode 简单】 第一百零八题 找到所有数组中消失的数字

    给定一个范围在  1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次. 找到所有在 [1, n] 范围之间没有出现在数组中的数字. 您能在不 ...

  6. 爬虫笔记之JS检测浏览器开发者工具是否打开

    在某些情况下我们需要检测当前用户是否打开了浏览器开发者工具,比如前端爬虫检测,如果检测到用户打开了控制台就认为是潜在的爬虫用户,再通过其它策略对其进行处理.本篇文章主要讲述几种前端JS检测开发者工具是 ...

  7. 关于禁用"请停用以开发者模式运行的扩展程序"提示

    有兴趣研究谷歌浏览器扩展开发,但每次开启高版本chrome都会有烦人的提示 因为我用的360急速,目前还有提示,但使用原生chrome连插件都给删了,无语

  8. python作业ATM(第五周)

    作业需求: 额度 15000或自定义. 实现购物商城,买东西加入 购物车,调用信用卡接口结账. 可以提现,手续费5%. 支持多账户登录. 支持账户间转账. 记录每月日常消费流水. 提供还款接口. AT ...

  9. python基础===一行 Python 代码实现并行(转)

    原文:https://medium.com/building-things-on-the-internet/40e9b2b36148 译文:https://segmentfault.com/a/119 ...

  10. echarts 移动端地图数据可视化开发教程

    如上效果图: 以下未代码: <!doctype html> <html lang="en">   <head> <meta charset ...