Java线程安全与锁优化
线程安全的严谨定义:
当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交题执行,也不需要进行额外的同步,或者调用方法进行其他任何操作,调用这个对象的行为都可以或者正确的结果,那么这个对象是线程安全的!
java共享数据分类(5类)
1)不可变
2)绝对线程安全:不管运行环境如何,调用者都不需要任何额外的同步措施,java api中标注自己是线程安全的类,都不是绝对线程安全的
3)相对线程安全:就是我们通常意义上讲的线程安全,需要保证对这个对象的单独操作是线程安全的,我们在调用的时候不需要做额外的保障措施,但对于一些特定顺序的连续调用就需要调用端使用额外的保障措施,比如vector的线程安全的容器,其add,get,size方法都被synchronized修饰,但是当另外一个线程恰好在错误的时间删除一个元素,导致该元素已经不再可用的话,就会产生异常,余姚对删除元素操作锁定一下
4)线程兼容:指对象本身不是线程安全的,但是可以通过在调用端正确的使用同步手段保证对象在多线程环境下是线程安全的,我们平常说的一个类不是线程安全的,通常指的就是这种情况
5)线程对立:无论是否采取同步措施,都无法并发执行,比如两个不同的线程同时持有一个线程对象,一个尝试去中断线程,一个尝试去恢复线程,这种情况无论是否采取同步措施,都无法并发执行,还存在死锁的风险
线程安全的实现方法:
1.互斥同步(悲观锁,最大的问题就是线程阻塞和唤醒带来的性能问题)
1.1最基本的互斥同步就是synchroized关键字(可重入锁,非公平锁)(重量级锁,线程阻塞唤醒开销大)
一点优化:在通知系统阻塞线程前加入一段自旋等待过程,避免频繁切换到核心态
1.2 ReentrantLock(也是重入锁,默认下非公平锁),需要lock,unlock方法配合try/finally完成操作,相比于synchronized,ReentrantLock增加了3个高级功能:可中断,可实现公平锁,以及锁可以绑定多个条件
注意:多线程环境下,synchronized的吞吐量下降得厉害,而ReentrantLock则能基本保证在一个稳定水平,是因为synchronized还要很多优化的余地!!
2.非阻塞同步(乐观锁,基于冲突检测的乐观并发策略)
通俗的说,就是先进行操作,如果没有其他线程竞争共享数据,那操作就成功了,如果共享数据有争用,产生了冲突,就是采取其他措施(最常见的就是不断的重试,直到成功,CAS机制),这种措施不需要挂起线程
从硬件方面保证操作和冲突检测具备原子性(unsafe类)
重量级锁的锁优化技术(Synchronized):
1.自旋锁:某个线程占用了共享数据,本线程先不挂起,而是处于自旋等待状态,不断重试
自旋的次数有限制,不然一直自旋会消耗系统资源
自适应自旋:如果某个锁,自旋很少成功获得,那么以后要获取这个锁时将可能省略掉自旋过程,以避免浪费处理器资源
2.锁消除:指JVM运行时,对一些代码上要求同步,但被检测到不可能存在共享数据竞争的锁进行消除
3.锁粗化:如果一系列的连续操作都对同一个对象反复加锁解锁,甚至是加锁操作出现在循环体中,那即使没有竞争,频繁的进行互斥同步操作也会导致不必要的性能消耗,这个时候将锁的范围扩展,变成一个锁,就是锁的粗化
4.轻量级锁:与Mark Word和CAS机制有关
轻量级锁能提供性能的依据:对于绝大部分,在整个同步周期内是不存在竞争的,这是一个经验数据
Mark Word的组成:

轻量级锁的加锁过程:
1)在代码进入同步块的时候,如果同步对象锁为无锁状态(锁标志位为01状态),虚拟机首先在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储旧的Mark Work的拷贝(锁记录解锁的时候用到)
2)虚拟机使用CAS机制尝试将对象的Mrak Word更新为轻量级锁的标志位和指向锁记录的指针
3)如果更新操作成功,那么线程就拥有了该对象的锁
4)如果这个更新操作失败,虚拟机首先会检查当前线程是否已经有了这个对象的锁,如果已经有了,就进入同步代码块继续执行,如果没有就说明该对象的锁被其他线程占用了,一旦这样,轻量级锁就膨胀成为重量级锁(比如synchronized),Mark Work中存储的就指向重量级锁的指针,后面等待锁的线程也会进入等待状态
轻量级锁的解锁过程:
1).通过CAS机制尝试将当前线程栈帧中的锁记录替换当前的Mark Word
2).如果替换成功,那么整个同步过程就完成了
3).如果替换失败,则说明有其他线程尝试获取过该锁,但失败了,导致轻量级锁变成了重量级锁,那么要在释放锁的同时,唤醒被挂起的线程
总结:轻量级锁就是在无竞争的情况下使用CAS操作区消除同步使用的互斥量
5.偏向锁:在无竞争的情况下,把整个同步过程都消除掉,连CAS操作都不做
偏向锁的依据:锁总是同一个线程持有,很少发生竞争
偏向锁偏向于第一个获得它的线程,如果在接下来的指向过程中,该锁没有被其他线程获取,则持有偏向锁的线程永远不需要进行同步
做法:只需要在锁第一个被拥有的时候,记录下偏向线程ID,这样偏向线程就一直持有着锁,直到竞争发生才释放锁,以后每次同步,检查锁的偏向线程ID是否与当前线程ID一致,如果一致直接进入同步,退出同步也无需每次加锁解锁都去CAS更新Mark Word,如果不一致则意味着发生了竞争,锁已经不总是偏向于一个线程了,这时候锁膨胀为重量级锁才能保证线程公平竞争锁
分析:引入偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行,因为轻量级锁的释放和获取依赖多次的CAS操作,而偏向锁只需要在置换线程ID的时候依赖一次CAS(由于一旦出现多线程竞争的情况就必须撤销偏向锁,所以偏向锁的撤销操作的性能损耗必须小于节省下来的CAS消耗),轻量级锁是为了在线程交替执行同步块时提高性能,而偏向锁则是在只有一个线程执行同步块时提高性能!
偏向锁加锁过程:
偏向锁加锁发生在偏向线程第一次进入同步块的时候,CAS操作尝试更新对象的Mrak Word(锁标志位为1,记录偏向线程的ID)
撤销偏向锁等待过程:
当有另外一共线程来竞争锁时,就需要将偏向锁膨胀为重量级,竞争线程尝试CAS更新Mark Work失败,会等到安全局点(此时不会执行任何代码)撤销偏向锁
Java线程安全与锁优化的更多相关文章
- Java线程安全与锁优化,锁消除,锁粗化,锁升级
线程安全的定义 来自<Java高并发实战>"当多个线程访问一个对象的时候,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方法的时候进行任何 ...
- Java虚拟机--线程安全和锁优化
Java虚拟机--线程安全和锁优化 线程安全 线程安全:当多线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象 ...
- 深入理解java虚拟机-第13章-线程安全与锁优化
第十三章 线程安全与锁优化 线程安全 java语言中的线程安全 1 不可变.Immutable 的对象一定是线程安全的 2 绝对线程安全 一个类要达到不管运行时环境如何,调用者都不需要额外的同步措施, ...
- 《深入理解Java虚拟机》-----第13章 线程安全与锁优化
概述 在软件业发展的初期,程序编写都是以算法为核心的,程序员会把数据和过程分别作为独立的部分来考虑,数据代表问题空间中的客体,程序代码则用于处理这些数据,这种思维方式直接站在计算机的角度去抽象问题和解 ...
- JVM之java并发 ——线程安全与锁优化
概述 人们很难想象现实中的对象在一项工作进行期间,会被不停地中断和切换,对象的属性(数据)可能会在中断期间被修改和变“脏”,而这些事情在计算机世界中则是很正常的事情.有时候,良好的设计原则不得不向现实 ...
- 深入理解Java虚拟机(第三版)-14. 线程安全与锁优化
14. 线程安全与锁优化 1. 什么是线程安全? 当多个线程同时访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替进行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个 ...
- 《深入了解java虚拟机》高效并发读书笔记——Java内存模型,线程,线程安全 与锁优化
<深入了解java虚拟机>高效并发读书笔记--Java内存模型,线程,线程安全 与锁优化 本文主要参考<深入了解java虚拟机>高效并发章节 关于锁升级,偏向锁,轻量级锁参考& ...
- JVM-并发-线程安全与锁优化
线程安全与锁优化 1.线程安全 (1)当多个线程访问一个对象时,如果不考虑这些线程在执行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获 ...
- jvm(13)-线程安全与锁优化(转)
0.1)本文部分文字转自“深入理解jvm”, 旨在学习 线程安全与锁优化 的基础知识: 0.2)本文知识对于理解 java并发编程非常有用,个人觉得,所以我总结的很详细: [1]概述 [2]线程安全 ...
随机推荐
- 2018-08-22 为中文API的简繁转换库添加迟到的持续集成
前两天在V2EX偶遇反馈帖第一次见以汉字命名的 Java 类 - V2EX, 于是复习了一下半年多没碰的项目program-in-chinese/zhconverter. 前文介绍了发布过程: 在Ma ...
- iphone手机怎么录屏 两种方法任你挑选
iphone手机怎么录屏呢?苹果手机拥有独特的Airlay镜像投屏,AirPlay的工作原理是当iPhone或IPAD与支持AirPlay技术的硬件,比如Apple TV等设备处在同一个wife的情况 ...
- 用ABP只要加人即可马上加快项目进展(二) - 分工篇
2018年和1998年其中两大区别就是: 前端蓬勃发展, 前后端分离是一个十分大的趋势. 专门的测试人员角色被取消, 多出了一个很重要的角色, 产品经理 ABP只要加入即可马上加快项目进展, 选择 ...
- 配置多个相同网段的ECMP下一跳,配合NQA健康检查实现高可靠性
1.一般情况下,ECMP常用的常见是,针对很远的目的地址,下一跳分别是路由器的不同出端口,而路由器的不同端口是不同网段的,也就是说,下一跳是不同的网段地址. 但是,在连接到终端服务器时,常常会采用多个 ...
- vue 构建项目遇到的问题
1.我在打包完成后,打开index.html文件发现地址并没有携带路由. config下的 index.js 中的build命令的配置有一个属性叫assetsPublicPath,它的值为‘/’.意思 ...
- mac下编译node源码
看过一篇win7 64x下面编译node的文章,链接地址:编译nodejs及其源码研究 下面学习一下在mac下面如何编译node源码. 过程也挺简单. 1.下载源码. > mkdir nodes ...
- 第一次使用VS Code时你应该知道的一切配置
前言 本文最新内容将在GitHub上实时更新. VS Code 本来是前端人员专用,但由于它实在是太好用了,于是,各种开发方向的码农也正在用 VS Code 作为他们的主力编程工具.甚至是一些写作的同 ...
- Python lambda介绍
转自:http://www.cnblogs.com/evening/archive/2010/03/29/2423554.html Python lambda 介绍 在学习python的过程中,l ...
- Windows服务器防火墙配置规范
本文属于一篇内部规范文档,整理的初衷是为了规范.统一集团的Windows服务器(仅仅SQL Server数据库服务器)防火墙设置,仅仅供内部其它同事设置Windows防火墙时作为参考的文档资料.如有不 ...
- sql server 计算两个时间 相差的 几天几时几分几秒
CAST ( CAST ( DATEDIFF ( ss, StartTime, ConcludeTime ) / ( 60 * 60 * 24 ) AS INT ) AS VARCHAR ) + '天 ...