Java虚拟机在操作系统层面会先尽一切可能在虚拟机层面上解决竞争关系,尽可能避免真实的竞争发生。同时,在竞争不激烈的场合,也会试图消除不必要的竞争。实现这些手段的方法包括:偏向锁、轻量级锁、自旋锁、锁消除、锁膨胀等

1. 偏向锁

偏向锁是JDK1.6提出的一种锁优化方式。其核心思想是:如果程序没有竞争,则取消之前已经取得锁的线程同步操作。也就是说,若某一锁被线程获取后,便进入偏向模式,当线程再次请求这个锁时,无需再进行相关的同步操作,从而节省了操作时间。如果在此之间有其他线程进行了锁请求,则锁退出偏向模式。在JVM中使用-XX:+UseBiasedLocking 可以设置启用偏向锁。

偏向锁在锁竞争激烈的场合没有太强的优化效果。

2. 轻量级锁

如果偏向锁失败,Java虚拟机会让线程申请轻量级锁。轻量级锁在虚拟机内部,使用一个称为BasicObjecLock的对象实现,这个对象内部由一个BasicLock对象和一个持有该锁的Java对象指针组成。BasicobjectLock对象放置在Java栈的栈帧中。

首先,BasicLock通过set_displaced_header()方法备份了原对象的Mark Word。接着,使用CAS操作,尝试将BasicLock的地址复制到对象头的Mark Word。如果复制 成功,那么加锁成功。如果加锁失败,那么轻量级锁就有可能被膨胀为重量级锁。

3. 锁膨胀

当轻量级锁失败时,就会膨胀成重量级锁。轻量级锁加锁失败后,虚拟机会执行以下操作:1. 废弃前面BasicLock备份的对象头信息。2. 通过inflate()方法进行锁膨胀,其目地是获得对象的ObjectMonitor  3. 使用enter()方法尝试进入锁。

4. 自旋锁

锁膨胀后,进入ObjectMonitor的enter(),线程很可能会在操作系统层面被挂起,这样线程上下文切换的性能损失就比较大。因此,在锁膨胀之后,虚拟机会做最后的争取,希望线程可以尽快进入临界区而避免被操作系统挂起。一种较为有效的手段就是使用自旋锁。

自旋锁可以使线程在没有取得锁时,不被挂起,而转去执行一个空循环(即所谓的自旋),在若十个空循环后,线程如果可以获得锁,则继续执行。若依然不能获得锁,才会被挂起。不太适用于锁竞争激烈的场合。

在JDK 1.6中,Java虚拟机提供 -XX:+UseSpinning 参数来开户自旋锁,使用 -XX:PreBlockSpin 参数来设置自旋锁的等待次数。

在JDK1.7中,自旋锁参数被取消。自旋锁总是会执行,自旋次数也由虚拟机自行调整。

5. 锁消除

锁消除是Java虚拟机在JIT编译时,对运行上下文的扫描,运用逃逸技术,去除不可能存在共享资源竞争的锁。比如:

public void someBusiness(){
......
StringBuffer sb = new StringBuffer();
sb.append("XXX");
......
}

StringBuffer中的方法都是有synchronized的,但是这里只是方法内的局部变量,不可能存在锁竞争。

逃逸分析和锁消除分别可以使用参数 -XX:+DoEscapeAnalysis 和 -XX:+EliminateLocks 开启(锁消除必须工作在 -server模式下)

《深入理解Java虚拟机》笔记04 -- 并发锁的更多相关文章

  1. Java内存区域与内存溢出异常——深入理解Java虚拟机 笔记一

    Java内存区域 对比与C和C++,Java程序员不需要时时刻刻在意对象的创建和删除过程造成的内存溢出.内存泄露等问题,Java虚拟机很好地帮助我们解决了内存管理的问题,但深入理解Java内存区域,有 ...

  2. 深入理解java虚拟机笔记Chapter12

    (本节笔记的线程收录在线程/并发相关的笔记中,未在此处提及) Java内存模型 Java 内存模型主要由以下三部分构成:1 个主内存.n 个线程.n 个工作内存(与线程一一对应) 主内存与工作内存 J ...

  3. 深入理解Java虚拟机笔记

    1. Java虚拟机所管理的内存 2. 对象创建过程 3. GC收集 4. HotSpot算法的实现 5. 垃圾收集器 6. 对象分配内存与回收细节 7. 类文件结构 8. 虚拟机类加载机制 9.类加 ...

  4. 深入理解java虚拟机笔记Chapter7

    虚拟机类的加载机制 概述 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类的加载机制. 类加载的时机 J ...

  5. 深入理解java虚拟机笔记之一

    Java的技术体系主要有支撑java程序运行的虚拟机,提供各开发领域接口支持Java API,java编程语言及许多第三方java框架( 如Spring,Structs等)构成. 可以把Java程序设 ...

  6. 深入理解Java虚拟机笔记——虚拟机类加载机制

    目录 概述 动态加载和动态连接 类加载的时机 类的生命周期 被动引用 例子一(调用子类继承父类的字段) 例子二(数组) 例子三(静态常量) 类加载的过程 加载 验证 准备 解析 符号引用 直接引用 初 ...

  7. 【转载】深入理解Java虚拟机笔记---运行时栈帧结构

    栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区的虚拟机栈(Virtual Machine Stack)的栈元素.栈帧存储了方法的局部变量表,操作 ...

  8. 深入理解java虚拟机笔记Chapter8

    运行时栈帧结构 栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈(Virtual Machine Stack)的栈元素.栈帧存储了方法 ...

  9. 深入理解java虚拟机笔记Chapter2

    java虚拟机运行时数据区 首先获取一个直观的认识: 程序计数器 线程私有.各条线程之间计数器互不影响,独立存储. 当前线程所执行的字节码行号指示器.字节码解释器工作时通过改变这个计数器值选取下一条需 ...

随机推荐

  1. python manage.py shell 的增删改查

    python manage.py shell 的增删改查 guoguo-MacBook-Pro:myblog guoguo$ python manage.py shell Python 3.5.1 ( ...

  2. 升级pip3的正确姿势

    如果你的电脑里装了两个python,就会有两个pip,一个是pip2,一个是pip3,还有可能出现一个既没有2也没有3的pip,一般情况下,pip等于pip2 有时候我们使用pip安装东西会提示我们p ...

  3. 理解Java泛型 通配符 ? 以及其使用

    什么是泛型: 泛型从字面上理解,是指一个类.接口或方法支持多种类型,使之广泛化.一般化和更加通用.Java中使用Object类来定义类型也 能实现泛型,但缺点是造成原类型信息的丢失,在使用中容易造成C ...

  4. png24 png8 jpeg与gif

    png24无损压缩,支持透明,有8位布尔透明通道,支持半透明,生成的图片品质最高,也最大 png8支持透明,有1位布尔透明通道,要么透明.要么半透明,生成的图片很小,只支持256色 jpeg有损压缩, ...

  5. 《avascript 高级程序设计(第三版)》 ---第三章 基本概念2

    1.乘性操作符: 1)*法操作法: Infinity * 0 = NaN  Infinity * 非零 = Infinity 或 - Infinity   2)/法操作符: Infinity / In ...

  6. 磁卡ID卡IC卡的区别【转】

    本文转载自:https://blog.csdn.net/trap94/article/details/50614451 今天被一个朋友问到ID卡和IC卡有什么区别,还真给问住了.虽然平时经常用到这些卡 ...

  7. Spring Boot2.0之 监控管理

    Spring boot监控中心: 针对微服务的服务状态,服务器的内存变化(内存.线程.日志管理等)检测服务配置连接地址是否有用(有些懒加载的情况下,用的时候发现卧槽不能用)模拟访问,懒加载.统计有多少 ...

  8. ORA-00600: internal error code, arguments: [6749], [3], [12602196]

    环境信息:Linux5.8 oracle10.2.0.4 问题现象: 现象1:alert日志有大量下面的错误信息: Wed Aug 27 21:01:27 2014Errors in file /u0 ...

  9. spring2.5和struts1.3.8整合

    第一步:导入对应jar文件 第二步: 1.在web容器中实例化spring容器 <!-- 指定spring的配置文件,默认从web根目录寻找配置文件,我们可以通过spring提供的classpa ...

  10. Django:locals()小技巧

    locals()返回一个包含当前作用域里面的所有变量和它们的值的字典. 所以可以把views改写为 def current_datetime(request):     current_date = ...