线程安全

1.定义

当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果

2.分类

(1)不可变

不可变的对象一定是线程安全的,只要一个不可变对象被正确地构建出来(没有发生this引用逃逸情况),那其外部的可见状态永远也不会改变 ,永远也不会看到它在多个线程之中处于不一致的状态。如Integer类,内部状态变量value定义为final来保障状态不变。

例1: JDK 中 Integer 类的构造函数

  1. private final int value;
  2. /**
    • Constructs a newly allocated {@code Integer} object that
    • represents the specified {@code int} value.
  3. * @param value the value to be represented by the
    •              {@code Integer} object.
  4. */

  5. public Integer(int value) {

  6. this.value = value;

  7. }

(2)绝对线程安全

要达到不管运行的环境如何,调用者都不需要任何额外的同步措施。在Java API中标注自己是线程安全的类,大多数都不是绝对线程安全的。

例2:加入同步以保证 Vector 访问的线程安全性

  1. Thread removeThread = new Thread(new Runnable() {
  2. @Override
  3. public void run() {
  4. synchronized(vector) {
  5. for (int i = 0; i < vector.size(); i++) {
  6. vector.remove(i);
  7. }
  8. }
  9. }
  10. });
  11. Thread printThread = new Thread(new Runnable() {

  12. @Override

  13. public void run() {

  14. synchronized(vector) {

  15. for (int i = 0; i < vector.size(); i++) {

  16. System.out.println(vector.get(i));

  17. }

  18. }

  19. }

  20. });
  21.  

(3)相对线程安全

通常意义上的线程安全,需要保证对这个对象的单独操作是线程安全的,在调用的时候不需要做额外的保障措施,但对于一些特定顺序的连续调用,就可能需要在调用端使用额外的同步手段来保证调用的正确性。如Vector、HashTable、Collections的synchronizedCollection()方法包装的集合。

(4)线程兼容

指对象本身不是线程安全的,但是可以通过调用端正确地使用同步手段来保证对象在并发环境中可以安全地使用,Java API大部分类属于线程兼容,如ArrayList、HashMap。

(5)线程对立

无论调用端是否采取了同步措施,都无法在多线程环境下并发使用的代码。有害的,应该尽量避免,如Thread类的suspend()和resume()方法,如果两个线程同时持有一个线程对象,一个尝试去中断线程,一个尝试去恢复线程,并且并发进行,无论调用时是否进行了同步,目标线程都存在死锁风险,所以这两个方法已经被声明废弃。

锁优化

1.定义

在阻塞式的情况下,如何让性能不要变得太差

2.锁的分类

锁分为:轻量级锁、自旋锁、偏向锁、

  • 对象头:描述对象的hash、锁信息、垃圾回收标记。指向锁记录的指针、指向monitor的指针、GC标记、偏向锁线程ID

(1)偏向锁:锁会偏向于当前已经占有锁的线程

  • 将对象头Mark的标记设置为偏向,并将线程ID写入对象头Mark

  • 只要没有竞争,获取偏向锁的线程,在将来进入同步块时,不需要同步

  • 当其他线程请求相同的锁时,偏向模式结束

  • -XX:+UseBiasedLocking 默认启用

  • 在竞争激烈的场合,偏向锁会增加系统的负担

(2)轻量级锁

  • 在代码进入同步块的时候,如果此同步对象没有被锁定,虚拟机首先将在当前线程的栈帧中建立一个名为锁记录的空间,用于存储锁对象目前的Mark Word的拷贝,称为Displaced Mark Word,

  • 然后使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针,如果更新动作成功,那么线程就拥有了该对象的锁,并且对象Mark Word的锁标志位转变成“00”,表示处于轻量级锁定状态。

  • 如果更新动作失败,先检查对象的Mark Word是否指向当前线程的栈帧,如果是,就可以直接进入同步块继续执行,否则说明这个锁对象被其他线程抢占了,如果有两条以上线程争用同一个锁,那轻量级锁就不再有效,要膨胀为重量级锁,锁标志的状态值转变为“10”。

  • 解锁过程也是通过CAS操作进行,如果对象的Mark Word仍然指向线程的锁记录,就用CAS操作把对象当前的Mark Word和线程中复制的Displaced Mark Word替换回来,

  • 如果替换成功,整个同步过程就完成了,如果替换失败,说明有其他线程尝试过获取锁,那就要在释放锁的同时,唤醒被挂起的线程。

  • 轻量级锁使用CAS操作避免使用互斥量的开销,如果存在锁竞争,除了互斥量的开销,还额外发生了CAS操作。

(3)自旋锁

  • 若线程可以很快获得锁,不用在OS挂起锁,让线程自旋。

  • 共享数据的锁定状态可能只持续很短的一段时间,为了这段时间去挂起和恢复线程不值得。如果物理机器有一个以上的处理器,能让两个或以上的线程同时并行执行,可以让后面请求锁的线程“等待一段时间”,但不放弃处理器的执行时间,看看持有锁的线程是否很快释放锁,为了让线程等待,让线程执行一个忙循环(自旋),这项技术就是自旋锁。

  • JDK 1.6引入自适应自旋,自旋时间不固定,由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行,那么虚拟机会认为这次自旋也很可能再次成功,进而允许自旋等待持续相对更长的时间,另外,如果对于某个锁,自旋很少成功,那么在以后获取这个锁时将可能省略掉自旋过程,以避免浪费处理器资源。

(4)内置于JVM中获取锁的步骤

① 偏向锁可用会尝试偏向锁

② 轻量级锁可用会尝试轻量级锁

③ 若①②失败,尝试自旋锁

④ 若①②③都失败,尝试普通锁

图解:

2.锁优化的方法

(1)减少锁的持有时间(方法锁改为锁对象)

例1

优化前:在进入方法前就要得到锁,其他线程就要在外面等待

  1. public synchronized void syncMethod(){
  2. othercode1();
  3. mutextMethod();
  4. othercode2();
  5. }

优化后:减少其他线程等待的时间

  1. public void syncMethod(){
  2. othercode1();
  3. synchronized(this)
  4. {
  5. mutextMethod();
  6. }
  7. othercode2();
  8. }

(2)减少锁粒度:将大对象(这个对象可能会被很多线程访问),拆成小对象,大大增加并行度,降低锁竞争

例:concurrentHashMap的分段锁

(3)锁分离:读写锁

例:LinkedBlockingQueue

从头部取出,从尾部放数据

(4)锁粗化:使用完公共资源后,立即释放锁

如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁出现在循环体中,那即使没有线程竞争,频繁地进行互斥同步操作也会导致不必要的性能损耗。如果虚拟机检测到有一串零碎的操作都对同一个对象加锁,将会把加锁同步的范围扩展到整个操作序列的外部。

例2:

优化前:

  1. public void demoMethod(){
  2. synchronized(lock){
  3. //do sth.
  4. }
  5. //做其他不需要的同步的工作,但能很快执行完毕
  6. synchronized(lock){
  7. //do sth.
  8. }
  9. }

优化后:

  1. public void demoMethod(){
  2. //整合成一次锁请求
  3. synchronized(lock){
  4. //do sth.
  5. //做其他不需要的同步的工作,但能很快执行完毕
  6. }
  7. }

(5)锁清除:

指在虚拟机即时编译器运行时,对一些代码上要求同步,但被检测到不可能存在共享数据竞争的锁进行消除,判定依据来源于逃逸分析的数据支持,如果一段代码,堆上的所有数据都不会逃逸出去被其他线程访问到,那就可以把它们当做栈上数据对待,认为是线程私有的,同步加锁无需进行。

(6)无锁:无锁的实现方式(CAS)

本人才疏学浅,若有错,请指出
谢谢!

[转帖]【JVM】线程安全与锁优化的更多相关文章

  1. 深入理解JVM - 线程安全与锁优化 - 第十三章

    线程安全 当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方法进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那么这个对 ...

  2. 一夜搞懂 | JVM 线程安全与锁优化

    前言 本文已经收录到我的 Github 个人博客,欢迎大佬们光临寒舍: 我的 GIthub 博客 学习导图 一.为什么要学习内存模型与线程? 之前我们学习了内存模型和线程,了解了 JMM 和线程,初步 ...

  3. jvm(13)-线程安全与锁优化(转)

    0.1)本文部分文字转自“深入理解jvm”, 旨在学习 线程安全与锁优化 的基础知识: 0.2)本文知识对于理解 java并发编程非常有用,个人觉得,所以我总结的很详细: [1]概述 [2]线程安全 ...

  4. jvm(13)-线程安全与锁优化

    [0]README 0.1)本文部分文字转自“深入理解jvm”, 旨在学习 线程安全与锁优化 的基础知识: 0.2)本文知识对于理解 java并发编程非常有用,个人觉得,所以我总结的很详细: [1]概 ...

  5. 深入理解JVM(7)——线程安全和锁优化

    Java中的线程安全 按照线程安全的“安全程度”由强至弱来排序,可以将Java语中各种操作共享的数据分为以下5类:不可变. 绝对线程安全. 相对线程安全. 线程兼容和线程对立. 1.不可变 不变的对象 ...

  6. JVM之java并发 ——线程安全与锁优化

    概述 人们很难想象现实中的对象在一项工作进行期间,会被不停地中断和切换,对象的属性(数据)可能会在中断期间被修改和变“脏”,而这些事情在计算机世界中则是很正常的事情.有时候,良好的设计原则不得不向现实 ...

  7. 《深入了解java虚拟机》高效并发读书笔记——Java内存模型,线程,线程安全 与锁优化

    <深入了解java虚拟机>高效并发读书笔记--Java内存模型,线程,线程安全 与锁优化 本文主要参考<深入了解java虚拟机>高效并发章节 关于锁升级,偏向锁,轻量级锁参考& ...

  8. JVM-并发-线程安全与锁优化

    线程安全与锁优化 1.线程安全 (1)当多个线程访问一个对象时,如果不考虑这些线程在执行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获 ...

  9. Java虚拟机--线程安全和锁优化

    Java虚拟机--线程安全和锁优化 线程安全 线程安全:当多线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象 ...

  10. 深入理解java虚拟机-第13章-线程安全与锁优化

    第十三章 线程安全与锁优化 线程安全 java语言中的线程安全 1 不可变.Immutable 的对象一定是线程安全的 2 绝对线程安全 一个类要达到不管运行时环境如何,调用者都不需要额外的同步措施, ...

随机推荐

  1. 记录:excel导入导出js-xlsx,处理合并

    效果 前情提要 后端传excel坐标数据,前端自己处理模板,找资料后,选择直接载入xlsx方式. 准备工作 npm i xlsx import * as XLSX from 'xlsx' 方法一:数据 ...

  2. 如何利用动态配置中心在JavaAgent中实现微服务的多样化治理

    本文分享自华为云社区<如何利用动态配置中心在JavaAgent中实现微服务的多样化治理>,作者:华为云开源 . 一.前言 随着JavaAgent在微服务治理方面的广泛应用和发展,我们可以在 ...

  3. 云图说 | GPU共享型AI容器,让AI开发更普及

    摘要:容器以其独特的技术优势,已经成为业界主流的AI计算框架(如Tensorflow.Caffe)的核心引擎,为了进一步解决企业在AI计算性能与成本上面临的问题,华为云量身打造了AI容器产品. 容器以 ...

  4. 你应该知道的数仓安全——默认权限实现共享schema

    摘要: 一种典型客户场景是一些用户是数据的生产方,需要在schema中创建表并写入数据:而另一些用户是数据的消费方,读取schema中的数据做分析.使用Alter default privilege语 ...

  5. CG行业云渲染服务的演进之路

    摘要:影视动画.特效制作等行业渲染需求量增多,4K/6K以及各高分辨率会陆续成为主流,本地算力与存储资源已无法满足现有任务量.而随着大环境的演变,CG行业发展已进入发展快车道.来自赞奇科技的CEO金伟 ...

  6. 万物皆可Cassandra:HUAWEI Tag背后的神仙数据库

    摘要:在GaussDB(for Cassandra)的支撑下,HUAWEI Tag防丢精灵表现出了非常强大的核心竞争力. 本文分享自华为云社区<万物皆可Cassandra--HUAWEI Tag ...

  7. 开心档之C++ 数据封装

    C++ 数据封装 所有的 C++ 程序都有以下两个基本要素: **程序语句(代码):**这是程序中执行动作的部分,它们被称为函数. **程序数据:**数据是程序的信息,会受到程序函数的影响. 封装是面 ...

  8. Net 高级调试之十六:平台互用性及P/Invoke和内存泄漏调试

    一.简介 今天是<Net 高级调试>的第十六篇文章,也是这个系列的最后一篇文章了.既然是最后一篇文章,我需要在这里说明一下,我当前的这个系列,不是针对<Net 高级调试>这本书 ...

  9. 火山引擎 EMR StarRocks 场景案例分享

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 日前 ,火山引擎数智平台(VeDI)旗下产品 E-MapReduce(简称"EMR")正式上线 ...

  10. QE01/QA11/QA02屏幕增强

    1.业务需求 需要对来料检验增加"合格数量"和"不合格数量"字段,涉及三个增强开发 2.QE01\QE02\QE03\QE51N屏幕增强 增强表 增强点BADI ...