线程安全

1.定义

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

2.分类

(1)不可变

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

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

   private final int value;  

/**
  • Constructs a newly allocated {@code Integer} object that
  • represents the specified {@code int} value.
* @param value the value to be represented by the
  •              {@code Integer} object.

*/

public Integer(int value) {

this.value = value;

}

(2)绝对线程安全

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

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

Thread removeThread = new Thread(new Runnable() {
@Override
public void run() {
synchronized(vector) {
for (int i = 0; i < vector.size(); i++) {
vector.remove(i);
}
}
}
}); Thread printThread = new Thread(new Runnable() {

@Override

public void run() {

synchronized(vector) {

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

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

}

}

}

});

(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

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

public synchronized void syncMethod(){
othercode1();
mutextMethod();
othercode2();
}

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

public void syncMethod(){
othercode1();
synchronized(this)
{
mutextMethod();
}
othercode2();
}

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

例:concurrentHashMap的分段锁

(3)锁分离:读写锁

例:LinkedBlockingQueue

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

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

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

例2:

优化前:

public void demoMethod(){
synchronized(lock){
//do sth.
}
//做其他不需要的同步的工作,但能很快执行完毕
synchronized(lock){
//do sth.
}
}

优化后:

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

(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. 实例讲解基于Sermant快速开发服务治理插件

    本文分享自华为云社区<Sermant框架下的服务治理插件快速开发及使用指南>,作者: 华为云开源 . Sermant是基于Java字节码增强技术的云原生无代理服务网格,它具有非侵入.插件化 ...

  2. MyBatis中SQL语句优化小结

    摘要:MyBatis 作为一款优秀的持久层框架,它支持自定义SQL.存储过程以及高级映射. MyBatis 作为一款优秀的持久层框架,它支持自定义SQL.存储过程以及高级映射.它免除了几乎所有的 JD ...

  3. DTSE Tech Talk | 云原生架构下的数字身份治理实践

    摘要:由华为技术大咖VS派拉软件CTO为大家详解云原生架构下的身份管理平台,构建云安全数字身份入口. 本文分享自华为云社区<DTSE Tech Talk | 第4期:云原生架构下的数字身份治理实 ...

  4. AI贺新年,开发者的虎年这样过才有意思

    摘要:祝所有的开发者们新春快乐,万事如意迎新年,如虎添翼旺全年! 普通人拜年,发一个祝福微信.程序员拜年,运行一串代码,制作独一无二的拜年短视频. 普通人送祝福,新年快乐.程序员送祝福,信手捏来一首拜 ...

  5. DarkMode(2):深色模式解决方案——css颜色变量实现Dark Mode

    暗黑模式实现,最初的设计,就是参考之前的主题模式.所谓多套主题/配色/皮肤,就是我们很常见的换肤功能.换肤简单的实现就是更换 css实现不同样式呈现不同肤色. 之前做不同颜色的皮肤,暗黑模式可以单做其 ...

  6. Vue 应用程序性能优化:代码压缩、加密和混淆配置详解

    ​ 简介在 Vue 应用程序的开发中,代码压缩.加密和混淆是优化应用程序性能和提高安全性的重要步骤. Vue CLI 是一个功能强大的开发工具,它提供了方便的配置选项来实现这些功能.本文将介绍如何使用 ...

  7. 火山引擎DataLeap的Data Catalog系统公有云实践 (下)

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 Data Catalog公有云遇到的挑战 Data Catalog经历了一个从0到1在火山引擎公有云部署并逐步优化 ...

  8. 大数据 - ClickHouse

    https://clickhouse.com/ 概念 ClickHouse 是俄罗斯的 Yandex 于 2016 年开源的列式存储数据库(DBMS),使用 C++语言编写,主要用于在线分析处理查询( ...

  9. SpringBoot 项目实战 | 瑞吉外卖 Day05

    该系列将记录一份完整的实战项目的完成过程,该篇属于第五天 案例来自B站黑马程序员Java项目实战<瑞吉外卖>,请结合课程资料阅读以下内容 该篇我们将完成以下内容: 新增套餐 套餐信息分页查 ...

  10. JSP | application 对象详解 & 网站计数器实现

    原作者为 RioTian@cnblogs, 本作品采用 CC 4.0 BY 进行许可,转载请注明出处. 本篇学习自:C语言中文网,部分内容转载仅供学习使用. \[QAQ \] JSP applicat ...