一夜搞懂 | JVM 线程安全与锁优化
2021-02-21 00:21
标签:原子性 线程 含义 end 模型 长度 成功 设置 hashmap 本文已经收录到我的 Github 个人博客,欢迎大佬们光临寒舍: 我的 GIthub 博客 之前我们学习了内存模型和线程,了解了 并发编程的目的是为了让程序运行得更快,提高程序的响应速度,虽然我们希望通过多线程执行任务让程序运行得更快,但是同时也会面临非常多的挑战,比如像线程安全问题、线程上下文切换的问题、硬件和软件资源限制等问题,这些都是并发编程给我们带来的难题。 其中线程安全问题是我们最关心的问题之一,我们接下来主要就围绕着线程安全的问题来展开。 当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的 要求线程安全的代码都必须具备一个特征: 下面将按照线程安全的程度由强至弱分成五类 一定是线程安全的 如何实现: 1.如果共享数据是一个基本数据类型,只要在定义时用 2.如果共享数据是一个对象,最简单的方法是把对象中带有状态的变量都声明为 例子: 可分成两大手段: 本篇重点在虚拟机本身 同步:在多个线程并发访问共享数据时,保证共享数据在同一个时刻只被一个线程使用 互斥:是实现同步的一种手段,临界区、互斥量和信号量都是主要的互斥实现方式 两者关系:互斥是因,同步是果;互斥是方法,同步是目的 属于悲观并发策略(悲观锁),即认为只要不做正确的同步措施就肯定会出现问题,因此无论共享数据是否真的会出现竞争,都要加锁 最大的问题是进行线程阻塞和唤醒所带来的性能问题,也称为阻塞同步 使用方式: A.使用 原理:编译后会在同步块的前后分别形成 注意: ? 1.若明确指定了对象参数,则取该对象的 ? 2.否则,会根据 过程:执行 特别注意: 1. 2.同步块在已进入的线程执行完之前,会阻塞后面其他线程的进入 ? B.使用重入锁 ? 之前在 进阶之路 | 奇妙的 Thread 之旅中也提到过重入锁的使用,相信看过的读者还有一些印象 与 与 1.等待可中断:当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情 2.公平锁:多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。而 3.锁绑定多个条件:一个 A.可重入代码/ 纯代码 B.线程本地存储 解决并发的正确性之后,为了能在线程之间更『高效』地共享数据、解决竞争问题、提高程序的执行效率,下面介绍五种锁优化技术 注意:自旋等待不能代替阻塞,它虽然能避免线程切换的开销,但会占用处理器时间,因此自旋等待的时间必须要有一定的限度,如果自旋超过了限定的次数(默认 首先先理解 加锁过程: 1.代码进入同步块时,如果同步对象未被锁定(锁标志位为 2.之后虚拟机会尝试用 另外,如果有两条以上的线程争用同一个锁,那轻量级锁就不再有效,要膨胀为重量级锁,锁标志位变为 解锁过程:若对象的 优点:因为对于绝大部分的锁,在整个同步周期内都是不存在竞争的,所以轻量级锁通过使用 自旋锁和轻量级锁的关系: Q:处于轻量级锁状态时,会不会使用自旋锁这个竞争机制 A:线程首先会通过 如果说轻量级锁是在无竞争的情况下使用 那偏向锁就是在无竞争情况下把整个同步都消除掉 含义:偏向锁会偏向于第一个获得它的线程,如果在后面的执行中该锁没有被其他的线程获取,则持有偏向锁的线程将永远不需要再进行同步 加锁过程:启用偏向锁的锁对象在第一次被线程获取时, 解锁过程:当有另外的线程去尝试获取这个锁时,根据锁对象目前是否处于被锁定的状态,撤销偏向后恢复到未锁定 优点:可提高带有同步但无竞争的程序性能,但若程序中大多数锁总被多个线程访问,此模式就没必要了 能够写出高性能、高伸缩性的并发程序是一门艺术,而了解并发在底层是如何实现的,则是掌握这门艺术的前提,也是成长为高级程序员的必备知识! 加油吧!骚年!以梦为马,不负韶华! 如果文章对您有一点帮助的话,希望您能点一下赞,您的点赞,是我前进的动力 本文参考链接: 一夜搞懂 | JVM 线程安全与锁优化 标签:原子性 线程 含义 end 模型 长度 成功 设置 hashmap 原文地址:https://www.cnblogs.com/xcynice/p/jvm-xian-cheng-an-quan-yu-suo-you-hua.html前言
学习导图
一.为什么要学习内存模型与线程?
JMM
和线程,初步探究了 JVM
怎么实现并发,而本篇文章,我们的关注点是 JVM
如何实现高效二.核心知识点归纳
2.1 线程安全
2.1.1 定义
代码本身封装了所有必要的正确性保障手段(如互斥同步等),令调用者无须关心多线程的问题,更无须自己采取任何措施来保证多线程的正确调用。2.1.2 分类
final
关键字修饰final
(例如 String
类的实现)
Vector
、HashTable
、Collections#synchronizedCollection()
包装的集合等
Java API
中大部分类都是属于线程兼容的,如 ArrayList
和 HashMap
等
Thread
类的 suspend()
和 resume()
,一个尝试中断线程,一个尝试恢复线程,在并发条件下,有可能会造成死锁2.1.3 实现
1.互斥同步
synchronized
关键字:
monitorenter
和 monitorexit
这两个字节码指令,并通过一个 reference
类型的参数来指明要锁定和解锁的对象reference
synchronized
修饰的是实例方法还是类方法去取对应的对象实例或 Class
对象来作为锁对象monitorenter
指令时先要尝试获取对象的锁。若该对象没被锁定或者已被当前线程获取,那么锁计数器 + 1
;而在执行 monitorexit
指令时,锁计数器 - 1
;当锁计数器 = 0
时,锁就被释放;若获取对象锁失败,那当前线程会一直被阻塞等待,直到对象锁被另外一个线程释放为止synchronized
同步块对同一条线程来说是可重入的,不会出现自我锁死的问题ReentrantLock
:
synchronized
的相同:用法与 synchronized
很相似,且都可重入synchronized
的不同:synchronized
是非公平的,即在锁被释放时,任何一个等待锁的线程都有机会获得锁。ReentrantLock
默认情况下也是非公平的,但可以通过带布尔值的构造函数改用公平锁ReentrantLock
对象可以通过多次调用 newCondition()
同时绑定多个 Condition
对象。而在 synchronized
中,锁对象的 wait()
和 notify()
或 notifyAl()
只能实现一个隐含的条件,若要和多于一个的条件关联不得不额外地添加一个锁
synchronized
能实现需求的情况下,优先考虑使用它来进行同步。理由如下:
synchronized
是 Java
语法层面的同步,足够清晰简单Lock
必须由程序员确保在 finally
块中释放锁,而 synchronized
可以由 JVM
确保锁的自动释放2.非阻塞同步
CAS
)3.无同步方案
ThreadLocal
的读者,可以看下笔者之前写的一篇文章:进阶之路 | 奇妙的 Handler 之旅2.2 锁优化
2.2.1 适应性自旋
10
次)仍未成功获锁,就需要挂线程了
2.2.2 锁消除
2.2.3 锁粗化
2.2.4 轻量级锁
HotSpot
虚拟机的对象头的内存布局:分为两部分
Mark Word
,是实现轻量级锁和偏向锁的关键。如哈希码、GC
分代年龄等
01
),虚拟机会在当前线程的栈帧中建立一个名为 Lock Record
的空间,用于存储锁对象 Mark Word
的拷贝。如下图CAS
操作将对象的 Mark Word
更新为指向 Lock Record
的指针。若更新动作成功,那么当前线程就拥有了该对象的锁,且对象 Mark Word
的锁标志位变为 00
,即处于轻量级锁定状态;反之,虚拟机会先检查对象的 Mark Word
是否指向当前线程的栈帧,若是,则当前线程已有该对象的锁,可直接进入同步块继续执行,否则说明改对象已被其他线程抢占。如下图:
10
,Mark Word
中存储的就是指向重量级锁的指针,后面等待锁的线程也要进入阻塞状态
Mark Word
仍指向着线程的 Lock Record
,就用 CAS
操作把对象当前的 Mark Word
和线程中复制的 Displaced Mark Word
替换回来。若替换成功,那么就完成了整个同步过程;反之,说明有其他线程尝试获取该锁,那么就要在释放锁的同时唤醒被挂起的线程CAS
操作消除同步使用的互斥量
CAS
获取锁,失败后通过自旋锁来尝试获取锁,再失败锁就膨胀为重量级锁。所以轻量级锁状态下可能会有自旋锁的参与(CAS
将对象头的标记指向锁记录指针失败的时候)2.2.5 偏向锁
CAS
去消除同步使用的互斥量
Mark Word
的锁标志位会被设置为 01
,即偏向模式,同时使用 CAS
操作把获取到这个锁的线程 ID
记录在对象的 Mark Word
中。若操作成功,持有偏向锁的线程以后每次进入这个锁相关的同步块时都可不再进行任何同步操作01
或轻量级锁定 00
的状态,后续的同步操作就如轻量级锁执行过程。如下图:三.碎碎念
上一篇:Spring---事务
下一篇:javascript输出
文章标题:一夜搞懂 | JVM 线程安全与锁优化
文章链接:http://soscw.com/index.php/essay/58221.html