Java虚拟机--线程安全和锁优化
Java虚拟机--线程安全和锁优化
线程安全
线程安全:当多线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那么这个对象就是线程安全的。
Java中,线程安全体现在多个线程访问同一个共享数据,如果一段代码中根本不会与其他线程共享数据,可以说不存在线程安全的问题。
线程安全的安全程度,由强至弱排序,可以分为以下5类。
不可变
不可变的对象一定是线程安全的,final关键字可以实现可见性。Java中如果共享数据时基本数据类型,加上final关键字就能保证它不可变,如果是对象,只需保证它是个不可变对象就行,比如String对象,对其的任何操作都不会改变原来的值,而是返回一个新的字符串对象。
绝对线程安全
满足一开始的线程安全定义,即:当多线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那么这个对象就是线程安全的。
相对线程安全
通常意义上的线程安全,需要保证这个对象单独操作是线程安全的,在调用时不需要做额外的保障措施,但是对于一些特定顺序的连续调用,就可能使用额外的同步手段。Java API中大多数线程安全类都是这种类型,比如Vector、HashTable等。
线程兼容
指对象本身不是线程安全,但是可以通过使用同步手段保证在并发环境下安全使用。通常说的线程不安全就是指这个类型,Java中大多数类都是,比如ArrayList、HashMap等。
线程对立
无论是否采取了同步措施,都无法在多线程环境中并发安全使用。在Java中很少见。
线程安全的实现方法
互斥同步
同步指在多线程并发访问共享数据时,保证共享的那个数据在同一时刻只能被一个线程使用。。在Java中使用synchronized或者concurrent包中的重入锁可以实现同步。后者相比前者,有一些更高级的功能:
- 等待可中断。当持有锁的线程长时间不释放锁,正在等待的线程可以不在放弃等待,从而可以处理其他任务。
- 公平锁。多个线程等待同一个锁,按照先来后到的顺序依次得到锁。而不是任意一个线程都有机会在这一次能得到锁。synchronized中的锁时非公平的,重入锁ReentrantLock默认非公平,不过可以指定参数设置为公平锁。
- 锁绑定多个条件。一个ReentrantLock可以和多个Condition对象绑定。
性能上,两者差不多。
非阻塞同步
同步会进行线程阻塞,属于悲观策略,即无论是否真的共享数据竞争,都要加锁。而CAS操作属于乐观策略,先进行操作,如果没有共享数据竞争,就操作成功;否则产生冲突,那么就不断重试,直到成功为止。
CAS操作有3个操作数,分别是内存位置V(Java中可简单理解为变量的内存地址)、旧的预期值A,和新值B,当且仅当V符合旧的预期值A时,才会用新值更新V的值,否则不更新,说明这个变量已经被别的线程修改过了。
CAS有个问题:如果变量V一开始被读取到是A值,中途被修改成B,最后又被修改回A,CAS操作会误认为变量V没有被改变过,这称为CAS操作的“ABA问题”。
无同步
如果不涉及共享数据,就无需进行同步。可重入代码:可以在代码执行的任何时刻中断,转而去执行其他代码,在控制权返回后,原来的程序不会出现任何错误。可重入代码都是线程安全的。
锁优化
自旋锁
如果共享数据的锁定状态只有很短的一段时间,为了这段时间去挂起和恢复线程(都需要转入内核态)并不值得,所以此时让后面请求锁的那个线程稍微等待以下,但不放弃处理器的执行时间。这里的等待其实就是执行了一个忙循环,这就是所谓的自旋。
自旋等待有一定的限度,默认值是10次,如果超过这个次数,就会使用传统方式挂起线程。JDK1.6中引入了自适应的自旋锁。如果在同一个对象上,自旋等待刚刚成功获得了锁,且持有的锁正在运行中,虚拟机就认为这次自旋也会成功,进而它被允许有更长时间的自旋等待;相反,如果对于某个锁,自旋很少成功过,那在之后获取这个锁时很可能省略掉自旋过程。
锁消除
虚拟机即时编译时,对一些代码上要求同步,但被检测到不可能存在共享数据竞争的锁进行消除。锁消除的依据来源于“逃逸分析”技术。堆上的所有数据都不会逃逸出去被其他线程访问到,就可以把它们当栈上的数据对待,认为它们是线程私有的,同步加锁就是没有必要的。
锁粗化
如果连续对某个对象反复枷锁或解锁,甚至加锁操作出现在循环中。虚拟机探测到这种情况,会把锁粗化到操作的外部。举个例子
public static int j = 0;
for (int i = 0; i < 100; i++) {
synchronized (this) {
j++;
}
}
// 锁粗化
synchronized (this) {
for (int i = 0; i < 100; i++) {
j++;
}
}
轻量级锁
传统的锁机制称为“重量级”锁,轻量级锁用于在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。
轻量级锁的加锁和解锁都是通过CAS操作完成的,如果有两个以上的线程争用同一个锁,轻量级锁将膨胀为重量级锁。
轻量级锁能提升同步性能主要因为:对于大多数锁,在整个同步周期内都是不存在竞争的。
偏向锁
轻量级锁噪无竞争的情况下使用CAS操作消除同步使用的互斥量,偏向锁是在无竞争的情况下吧整个同步都消除掉,CAS操作也没有了。
偏向锁会偏向第一个获得它的线程,如果在接下来的执行过程中,该锁没有被其他线程获取,则持有偏向锁的线程永远也不需要再进行同步。
by @sunhaiyu
2018.6.20
Java虚拟机--线程安全和锁优化的更多相关文章
- JVM之java并发 ——线程安全与锁优化
概述 人们很难想象现实中的对象在一项工作进行期间,会被不停地中断和切换,对象的属性(数据)可能会在中断期间被修改和变“脏”,而这些事情在计算机世界中则是很正常的事情.有时候,良好的设计原则不得不向现实 ...
- 深入理解java虚拟机-第13章-线程安全与锁优化
第十三章 线程安全与锁优化 线程安全 java语言中的线程安全 1 不可变.Immutable 的对象一定是线程安全的 2 绝对线程安全 一个类要达到不管运行时环境如何,调用者都不需要额外的同步措施, ...
- 【深入理解JAVA虚拟机】第5部分.高效并发.2.线程安全和锁优化
1 概述 对于这部分的主题“高效并发”来讲,首先需要保证并发的正确性,然后在此基础上实现高效. 2 线程安全 <Java Concurrency In Practice> 的作者Brian ...
- 《深入理解Java虚拟机》-----第13章 线程安全与锁优化
概述 在软件业发展的初期,程序编写都是以算法为核心的,程序员会把数据和过程分别作为独立的部分来考虑,数据代表问题空间中的客体,程序代码则用于处理这些数据,这种思维方式直接站在计算机的角度去抽象问题和解 ...
- 深入理解Java虚拟机(第三版)-14. 线程安全与锁优化
14. 线程安全与锁优化 1. 什么是线程安全? 当多个线程同时访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替进行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个 ...
- 《深入了解java虚拟机》高效并发读书笔记——Java内存模型,线程,线程安全 与锁优化
<深入了解java虚拟机>高效并发读书笔记--Java内存模型,线程,线程安全 与锁优化 本文主要参考<深入了解java虚拟机>高效并发章节 关于锁升级,偏向锁,轻量级锁参考& ...
- JVM-并发-线程安全与锁优化
线程安全与锁优化 1.线程安全 (1)当多个线程访问一个对象时,如果不考虑这些线程在执行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获 ...
- jvm(13)-线程安全与锁优化(转)
0.1)本文部分文字转自“深入理解jvm”, 旨在学习 线程安全与锁优化 的基础知识: 0.2)本文知识对于理解 java并发编程非常有用,个人觉得,所以我总结的很详细: [1]概述 [2]线程安全 ...
- 深入理解JVM(7)——线程安全和锁优化
Java中的线程安全 按照线程安全的“安全程度”由强至弱来排序,可以将Java语中各种操作共享的数据分为以下5类:不可变. 绝对线程安全. 相对线程安全. 线程兼容和线程对立. 1.不可变 不变的对象 ...
随机推荐
- .net中的SelectList在Html.DropdownList中的使用
.net中的SelectList可以用于前端下拉框的内容填充 譬如:Html.DropdownList(下拉框标签名称, SelectList实例) 实际上,上述Html.DropdownList的第 ...
- EF架构获取数据时报错:The ObjectContext instance has been disposed and can no longer be used for operations that require a connection. Do you want to correct the value?
场景: EF底层,获取完主表,点击按钮,添加主表,字表内容时,报以上错误 解决方案: 在EF文件的空白处右键--属性,将“应用延迟加载”,改为False
- underscore.js源码研究(6)
概述 很早就想研究underscore源码了,虽然underscore.js这个库有些过时了,但是我还是想学习一下库的架构,函数式编程以及常用方法的编写这些方面的内容,又恰好没什么其它要研究的了,所以 ...
- C#6.0语言规范(十一) 结构
结构与类类似,因为它们表示可以包含数据成员和函数成员的数据结构.但是,与类不同,结构是值类型,不需要堆分配.结构类型的变量直接包含结构的数据,而类类型的变量包含对数据的引用,后者称为对象. 结构对于具 ...
- Python小白学习之路(二十六)—【if __name__ =='__main__':】【用状态标识操作】
规则一: 一个python文件中,只写一些可以运行的功能测试代码写在这句代码下面 if __name__ =='__main__': 在讲这边的时候,我不是很懂参考了一篇博客,地址如下:http:// ...
- 6_文件IO
1. 基本文件读取 readline(),readlines(),write(),writelines() f.read(size),指定读取文件的字节数,需要注意的是 ...
- POJ 2606
#include<iostream> #include<set> #include<stdio.h> #include<math.h> #include ...
- 3DMax——室内设计:墙体+吊顶
1.导入CAD平面图 2.将导入的平面图全部选中→颜色设置为其他颜色→设置为组(设置为组,是为了后期选材质方便) 3.选中图形,选择移动工具,输入坐标为0,右键选择冻结当前选择 4.右键“角度捕捉切换 ...
- matlab中元胞数组的创建与内容读取
一.创建元胞数组 1.用cell命令创建规格为2*2的空元胞 >> a=cell(2,2) a = [] [] [] [] 2.用大括号"{}"创建元胞数组并赋值 &g ...
- Log4j 相关
Log4j(Log for Java) Log4j是Apache提供的一种专门用于Java程序记录日志的工具,是目前主流的开发日志技术. 日志的作用: 1.记录系统运行过程中的重要运行信息 a) 付费 ...