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时,才会有心智B更新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

原文地址:https://www.cnblogs.com/sun-haiyu/p/9204308.html

时间: 2024-11-06 04:37:54

Java虚拟机--线程安全和锁优化的相关文章

深入理解Java虚拟机(第三版)-14. 线程安全与锁优化

14. 线程安全与锁优化 1. 什么是线程安全? 当多个线程同时访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替进行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那就称这个对象是线程安全的 2. Java语言中的线程安全 我们将Java语言下的线程安全分为以下五类:不可变.绝对线程安全.相对线程安全.线程兼容和线程对立. 1.不可变:不可变一定是线程安全的,无论是对象的方法实现还是方法的调用者,都不需要进行任何线程安全保障措施

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

第十三章 线程安全与锁优化 线程安全 java语言中的线程安全 1 不可变.Immutable 的对象一定是线程安全的 2 绝对线程安全 一个类要达到不管运行时环境如何,调用者都不需要额外的同步措施,通常需要付出很大甚至是不切实际的代价,在java api中标注自己是线程安全的类,大多数都不是绝对的线程安全 3 相对线程安全 4 线程兼容  对象本身不是线程安全的,但是可以通过在调用端正确地使用同步手段来保证对象在并发环境中可以安全使用 5 线程对立 线程安全的实现方法 1 互斥同步 Murua

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

0.1)本文部分文字转自“深入理解jvm”, 旨在学习 线程安全与锁优化 的基础知识: 0.2)本文知识对于理解 java并发编程非常有用,个人觉得,所以我总结的很详细: [1]概述 [2]线程安全 1)线程安全定义:当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的:(干货——线程安全定义) [2.1]java 语言中的线程安全(干货——java

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

线程安全与锁优化 1.线程安全 (1)当多个线程访问一个对象时,如果不考虑这些线程在执行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那么这个对象时是线程安全的. (2)Java语言中的线程安全 a)可以将Java语言中各种操作共享的数据分为5类:不可变,绝对线程安全,相对线程安全,线程兼容和线程对立 b) 在Java语言中不可变的对象一定是线程安全的,无论是对象的方法实现还是方的调用者,都不需要再采用任何的线程安全

线程安全与锁优化

线程安全与锁优化 线程安全 java语言中的线程安全 不可变 相对线程安全 绝对线程安全 线程兼容 线程对立 线程安全的实现方法 互斥同步 非阻塞同步 无同步方案 锁优化 自旋与自适应自旋 锁消除 锁粗化 轻量级锁 偏向锁 线程安全与锁优化 线程安全 <Java Concurrency In Practice>的作者Brian Goetz对"线程安全"有一个比较恰当的定义:"当多个线程访问同一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进

Java虚拟机11:运行期优化

前言 http://www.cnblogs.com/xrq730/p/4839245.html,HotSpot采用的是解释器+编译器并存的架构,之前的这篇文章里面已经讲过了,本文只是把即时编译器这块再讲得具体一点而已.当然,其实本文的内容也没多大意义,90%都是概念上的东西,对于实际开发.实际解决项目里面的疑难问题并没有什么太大的帮助,只要看过就好了. 编译对象与触发条件 之前讲过,Sun使用的虚拟机之所以被叫做"HotSpot",就是因为运行过程中会检测热点代码,那么运行过程中,会被

书籍推荐:《实战Java虚拟机——JVM故障诊断与性能优化》下载

本书详细介绍Java虚拟机的基本原理和优化诊断方法.其中重点介绍Java虚拟机的体系结构.常用的虚拟机参数.Java虚拟机的垃圾回收原理.算法以及目前虚拟机所支持的各种垃圾回收器及其区别.特点和使用方法.在实践和调优方面,重点介绍了Java的堆.栈分析方法,性能调优的一般思路.手段和工具.此外,还详细介绍了虚拟机内有关"锁"的实现以及优化方法. 作为对虚拟机的深入了解,本书还将详细介绍Java类的基本格式.装载过程和虚拟机的执行机制. Java虚拟机目前是Java.Scala.Cloj

java线程安全和锁优化

面向对象的编程思想是站在现实世界的角度去抽象和解决问题,他把数据和行为都看作是对象的一部分,这样可以让程序员能以符合现实世界的思维方式来编写和组织程序. 线程安全的一个恰当的定义:当多个线程访问一个对象时,如果不用考虑这些线程在运行环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的 . 按照线程安全的安全程度由强至弱来排序,可以将java语言中各种操作共享的数据分为以下5类:不可变.绝对线程安全 相对

Java并发编程学习:线程安全与锁优化

本文参考<深入理解java虚拟机第二版> 一.什么是线程安全? 这里我借<Java Concurrency In Practice>里面的话:当多个线程访问一个对象,如果不考虑这些线程在运行时环境下的调度和交替执行,也不需要额外的同步,或者调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那么这个对象是线程安全的. 我的理解:多线程访问一个对象,任何情况下,都能保持正确行为,就是对象就是安全的. 我们可以将Java语言中各种操作共享的数据分为以下5类:不可变.