并发系列(一)-----synchronized关键字
一 简介
说到并发不得不提的synchronized,synchronized关键字是元老级别的角色。在Java SE 1.6之前synchronized被称为是重量,在1.6之后对同步进行了一系列的优化,使它的“重量”发生变化。这篇文章主要介绍同步的原理和它“重量”变化
二 表现形式
同步代码在表现的形式有三种同步在代码的表现形式有三种
1.对于同步方法,锁是当前实例对象(非静态方法)
2.对于静态同步方法,锁是当前类的类对象(静态方法)
3.对于同步方法块,锁是sysnchronized括号里配置的对象(代码块)
三 原理说明
在JVM(1.7)规范里面就说明了同步的原理
原文如下:Java虚拟机中的同步(同步)基于进入和退出管程(监视器)对象实现无论是显式同步(有明确的monitorenter和monitorexit指令)还是隐式同步(依赖方法调用和返回指令实现的)都是如此。在Java的语言中,可以被同步修饰的同步方法。标志来隐式实现的(参见§2.11.10“同步”)。
monitorenter和monitorexit指令用于实现同步语句块,譬如需要执行其对应的指令,而无论这个方法是正常结束(§2.6.4)还是异常结束(§2.6.5)。为了保证在方法异常完成时monitorenter和monitorexit指令依然可以正确配对执行,编译器会自动产生一个异常处理器(§2.10),这个异常处理器声明可处理所有的异常,它的目的就是用来执行monitorexit指令具体博客:JVM规范说明。
从上面JVM规范中可以看出JVM基于进入和退出监测对象来实现方法同步和代码块同步,两者的实现细节不一样。代码块同步是使用monitorenter和monitorexit指令实现的,用指令读取运行时常量池中方法的ACC_SYNCHRONIZED标志来隐式实现的.monitorenter 指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处理和异常处理,JVM要保证每个monitorenter必须有对应monitorexit与之配对。任何对象都有一个监视与之关联,当且一个显示器被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的监控的所有权,即尝试获得对象的锁。
四 锁的重量变化
Java SE 1.6为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”,在Java SE 1.6中,锁一共有4种状态,级别从低到高依次是:无锁状态,偏向锁状态,轻量级锁状态和重量级锁状态,这几个状态会随着竞争情况逐渐升级锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁
4.1 对象头和栈帧的简单介绍
在介绍锁的重量变化的前读者可能要明白一些关于JVM的相关知识这里为了好理解只是简单的介绍一下对象头和栈帧。
栈帧:方法在执行的同时都会创建一个栈帧(方法运行时的基础数据结构)用于存储局部变量表,操做数栈,动态链接,方法出口等信息每一个方法从调用直至执行完成的过程,就对应这一个栈帧在虚拟机栈中入栈到出栈的过程。局部变量表存放了编译器可知的各种基本数据类型,对象引用(regerence,它不等同与对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他于此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)
对象头:HotSpot虚拟机的对象头(Object Header)分为两部分信息,第一部分用于存储对象自身的运行时数据,如哈希码(HashCode),GC分代年龄(Generational GC Age)等,这部分数据的长度在32位和64位的虚拟机中分别为32个和64个位,官方称它为“Mark Word”,它是实现轻量级锁和偏向锁的关键。另外一部分用于存储指向方法区对象类型数据的指针,如果是数组对象的话,还会有一个额外的部分用于存储数组长度。
对象头信息是与对象自身定义的数据无关的额外存储成本,考虑到虚拟机的空间效率,Mark Word被设计成一个非固定的数据结构以便在极小的空间内存储尽量多的信息,它会根据对象的状态复用自己的存储空间例如。在32位的热点虚拟机中对象未被锁定的状态下,标记字的32个位空间中的25位用于存储对象哈希码(HashCode),4位用于存储对象分代年龄,2位用于存储锁标志位,1位固定为0,在其他状态(轻量级锁定,重量级锁定,GC标记,可偏向)下对象的存储内容如表所示。
4.2 偏向锁
偏向锁的“偏”,就是偏心的“偏”,偏袒的“偏”。它的意思是这个锁会偏向于第一个获得它的线程,如果在接下来的执行过程中,该锁没有被其他的线程获取,则持有偏向锁的线程将永远不需要再进行同步这里。这点可以从偏向锁的的撤销体现出来偏向锁的撤销采用的是一种等到竞争出现才释放的机制,也就是说如果没有竞争那么偏向锁一直都不会释放。
4.2.1锁定获取
当一个线程访问同步块并获取锁时,会在对象头(包含运行时数据和类型指针)和栈帧(一种数据结构)中的锁记录里存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需简单地测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。如果测试成功,表示线程已经获得了锁。如果测试失败,则需要再测试一下标记字中偏向锁的标识是否设置成1(表示当前是偏向锁):如果没有设置,则使用CAS竞争锁;如果设置了,则尝试使用CAS将对象头的偏向锁指向当前线程。
过程如下
(1)访问Mark Word中偏向锁的标识是否设置成1,锁标志位是否为01--确认为可偏向状态。
(2)如果为可偏向状态,则测试线程ID是否指向当前线程,如果是,进入步骤(5),否则进入步骤(3)。
(3)如果线程ID并未指向当前线程,则通过CAS操作竞争锁如果竞争成功,则将标志字中线ID设置为当前线程ID,然后执行(5);如果竞争失败,执行(4)。
(4)如果CAS获取偏向锁失败,则表示有竞争。当到达全局安全点(还原点详细信息参考JVM虚拟机)时获得偏向锁的线程被挂起,偏向锁升级为轻量级锁,然后被阻塞在安全点的线程继续往下执行同步代码。
(5)执行同步代码。
4.2.2偏向锁的撤销
偏向锁使用了一种等到竞争出现才释放锁的机制,所以当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。偏向锁的撤销,需要等待全局安全点(在这个时间点上没有正在执行的字节码)它会首先暂停拥有偏向锁的线程,然后检查持有偏向锁的线程是否活着,如果线程不处于活动状态,则将对象头设置成无锁状态;如果线程仍然活着,拥有偏向锁的栈会被执行,遍历偏向对象的锁记录,栈中的锁记录和对象头的标记字要么重新偏向于其他线程,要么恢复到无锁或者标记对象不适合作为偏向锁(这时就需要锁升级了),最后唤醒暂停的线程。
4.3轻量级锁
轻量级锁是JDK 1.6之中加入的新型锁机制,它名字中的“轻量级”是相对于使用操作系统互斥量来实现的传统锁而言的,因此传统的锁机制就被称为“重量级”锁。首先需要强调一点的是,轻量级锁并不是用来代替重量级锁的,它的本意是在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。
4.3.1轻量级的加锁
线程在执行同步块之前,JVM会先在当前线程的第一个中创用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中,官方称为DisplacedMark Word。然后线程尝试如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁。
4.3.2轻量级锁是解锁
轻量级解锁时,会使用原子的CAS操作将位移标记字替换回对象头,如果成功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁。
因为自旋会消耗CPU,为了避免无用的自旋(比如获得锁的线程被阻塞住了),一旦锁升级
成重量级锁,就不会再恢复到轻量级锁状态。当锁处于这个状态下,其他线程试图获取锁时,
都会被阻塞住,当持有锁的线程释放锁之后会唤醒这些线程,被唤醒的线程就会进行新一轮
的夺锁之争。
五 锁的有比较
六 说明
文章有差错地方希望大家指出,邮箱alemand@163.com
参考文档:
<< Java并发编程艺术>>方腾飞魏鹏程晓明
<<深入理解Java虚拟机:JVM高级特性与最佳实践>>周志明
<< Java虚拟机规范JavaSE1.7 >> Tim Lindholm,Frank Yellin, Gilad Bracha,Alex Buckley
https://www.tuicool.com/articles/2aeAZn
https://www.artima.com/insidejvm/ed2/threadsynch3.html
http://www.iteye.com/topic/1018932
并发系列(一)-----synchronized关键字的更多相关文章
- Java并发系列之Synchronized
每一个刚接触多线程并发编程的同学,当被问到,如果多个线程同时访问一段代码,发生并发的时候,应该怎么处理? 我相信闪现在脑海中的第一个解决方案就是用synchronized,用锁,让这段代码同一时间只能 ...
- Java并发编程之synchronized关键字
整理一下synchronized关键字相关的知识点. 在多线程并发编程中synchronized扮演着相当重要的角色,synchronized关键字是用来控制线程同步的,可以保证在同一个时刻,只有一个 ...
- Java 多线程并发编程之 Synchronized 关键字
synchronized 关键字解析 同步锁依赖于对象,每个对象都有一个同步锁. 现有一成员变量 Test,当线程 A 调用 Test 的 synchronized 方法,线程 A 获得 Test 的 ...
- 并发编程之synchronized关键字
synchronized关键字 synchronized关键字最主要的三种使用方式的总结 1.修饰实例方法,作用于当前对象实例加锁,进入同步代码块前要获得当前对象实例的锁 2.修饰静态方法,作用于当前 ...
- Java精通并发-自旋对于synchronized关键字的底层意义与价值分析以及互斥锁属性详解与Monitor对象特性解说【纯理论】
自旋对于synchronized关键字的底层意义与价值分析: 对于synchronized关键字的底层意义和价值分析,下面用纯理论的方式来对它进行阐述,自旋这个概念就会应运而生,还是很重要的,下面阐述 ...
- 【Java并发编程】Synchronized关键字实现原理
想必在面试中经常会被问到Synchronized关键字,它有什么特性,原理什么 它的主要特性是同步锁.非公平锁.阻塞锁.可以保证线程安全(可见性.原子性.有序性) JDK1.6之后对Synchroni ...
- Java精通并发-Lock与synchronized关键字在底层的区别及实例分析
在上两次中已经将Lock这个接口的整个官方说明进行了阅读,这次来了解一下它的一个非常重要的实现类: 啥叫“可重入”呢?其实是指一个线程已经拿到了锁,然后该线程还能再次获取这把锁,接下来在了解它之前先用 ...
- Java中synchronized关键字理解
好记性不如烂笔头~~ 并发编程中synchronized关键字的地位很重要,很多人都称它为重量级锁.利用synchronized实现同步的基础:Java中每一个对象都可以作为锁.具体表现为以下三种形式 ...
- java高并发系列 - 第10天:线程安全和synchronized关键字
这是并发系列第10篇文章. 什么是线程安全? 当多个线程去访问同一个类(对象或方法)的时候,该类都能表现出正常的行为(与自己预想的结果一致),那我们就可以所这个类是线程安全的. 看一段代码: pack ...
随机推荐
- 参数innodb_force_recovery影响了整个InnoDB存储引擎的恢复状况
参数innodb_force_recovery影响了整个InnoDB存储引擎的恢复状况.该值默认为0,表示当需要恢复时执行所有的恢复操作.当不能进行有效恢复时,如数据页发生了corruption,My ...
- oracle like模糊查询简单用法
like 用法介绍: 1.“_”:匹配单个任意字符 select * from bqh3 where name like '_崔'; 2.“%”:匹配0个或多个任意字符.但有三种情况如下: like ...
- 浏览器加载和渲染html的顺序-css渲染效率的探究(转载)
1.浏览器加载和渲染html的顺序1.IE下载的顺序是从上到下,渲染的顺序也是从上到下,下载和渲染是同时进行的.2.在渲染到页面的某一部分时,其上面的所有部分都已经下载完成(并不是说所有相关联的元素都 ...
- 【Alpha 冲刺】 12/12
今日任务总结 人员 今日原定任务 完成情况 遇到问题 贡献值 胡武成 完成app端api编写 已完成 JAVA后端跨域访问没有处理(目前已解决),导致前端localhost请求失败而误以为自己操作失误 ...
- jQuery 使用ajax,并刷新页面
<script> function del_product_information(id) { $.ajax({ url: "{% url 'del_product_inform ...
- PReLU与ReLU
PReLU激活函数,方法来自于何凯明paper <Delving Deep into Rectifiers:Surpassing Human-Level Performance on Image ...
- DNS主从复制及子域(三)
主从DNS 主辅DNS服务器数据同步的过程,首先master DNS服务器每 次修改完成并重启服务后,将传送notify给所有的Slave DNS服务器. Slave DNS服务器将查询Master服 ...
- 解决:linux 固定ip 导致ping 外网unknown host
首先说下问题产生场景:最近搞jenkins搭建持续集成,搞完后发现服务器ip(ifconfig 红色)老是变化,一怒之下果断修改ip [root@bogon etc]# ifconfigeth0 Li ...
- array_multisort函数,以及多维数组下排序的应用,并与usort函数对比
以前比较少用这个函数,大部分自己接触的业务里,处理稍微大一些的数组的时候几乎都是从db里取出来的,在db里就order by了. 最近倒是用了次,这个函数用来排序很强大,有点类似于sql中的order ...
- React Native创建一个APP
React Native 结合了 Web 应用和 Native 应用的优势,可以使用 JavaScript 来开发 iOS 和 Android 原生应用.在 JavaScript 中用 React 抽 ...