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虚拟机--线程安全和锁优化的更多相关文章

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

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

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

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

  3. 【深入理解JAVA虚拟机】第5部分.高效并发.2.线程安全和锁优化

    1 概述 对于这部分的主题“高效并发”来讲,首先需要保证并发的正确性,然后在此基础上实现高效. 2 线程安全 <Java Concurrency In Practice> 的作者Brian ...

  4. 《深入理解Java虚拟机》-----第13章 线程安全与锁优化

    概述 在软件业发展的初期,程序编写都是以算法为核心的,程序员会把数据和过程分别作为独立的部分来考虑,数据代表问题空间中的客体,程序代码则用于处理这些数据,这种思维方式直接站在计算机的角度去抽象问题和解 ...

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. Windows核心编程:第9章 用内核对象进行线程同步

    Github https://github.com/gongluck/Windows-Core-Program.git //第9章 用内核对象进行线程同步.cpp: 定义应用程序的入口点. // #i ...

  2. WPF学习笔记(4):获取DataGridTemplateColumn模板定义的内容控件

    在之前的DataGrid的DataGridTemplateColumn列中,自定义了一个TextBox控件,但是在C#代码中提示找不到这个控件,导致无法对该控件进行操作.在网上搜索后,发现一些处理方法 ...

  3. [leetcode.com]算法题目 - Jump Game

    Given an array of non-negative integers, you are initially positioned at the first index of the arra ...

  4. word2vec的原理(一)

    最近上了公司的新员工基础培训课,又对NLP重新产生的兴趣.NLP的第一步大家知道的就是不停的写正则,那个以前学的还可以就不看了.接着就是我们在把NLP的词料在传入神经网络之前的一个预处理,最经典的就是 ...

  5. PHP 获取两个时间之间的月份

    ## 获取两个时间之间的间距时间 $s = '2017-02-05'; $e = '2017-07-20'; $start = new \DateTime($s); $end = new \DateT ...

  6. Angular使用总结 --- 搜索场景中使用rxjs的操作符

    在有input输入框的搜索/过滤业务中,总会考虑如何减少发起请求频率,尽量使每次的请求都是有效的.节流和防抖是比较常见的做法,这类函数的实现方式也不难,不过终归还是需要自己封装.rxjs提供了各种操作 ...

  7. linux服务器的相关信息查看(端口占用,cpu、内存占用,防火墙,系统信息,vim编辑器使用等)

    一.端口占用情况   https://www.cnblogs.com/CEO-H/p/7794306.html (1)查看所有端口.进程的使用情况:netstat -tunlp (2)查看某一端口的使 ...

  8. iOS 8.0 bluetooth peripheral manager giving no callback for addService

    I am adding the service using: [self.peripheralManager addService:myService]; Is this method depreca ...

  9. 开发工具 -- Eclipse快捷键

    [ALT+/]此快捷键为用户编辑的好帮手,能为用户提供内容的辅助,不要为记不全方法和属性名称犯愁,当记不全类.方法和属性的名字时,多体验一下[ALT+/]快捷键带来的好处吧.   [Ctrl+O]显示 ...

  10. 移动键盘 滚动input

    window.addEventListener('resize', function () { if(document.activeElement.tagName === 'INPUT'){ docu ...