Android应用多线程

多线程知识梳理(3) - synchronized 三部曲之锁优

2017-05-03  本文已影响201人  泽毛

一、前言

多线程知识梳理(2) - synchronized 基本使用 中,我们介绍了使用重量锁来实现的synchronized。今天,我们就来一起学习一下在JDK 1.6之后,对synchronized所采取的一系列优化措施。

二、对象头 & Monitor Record

在介绍优化方法之前,我们需要介绍两个重要的概念Java对象头和Monitor

2.1 对象头

Java&Android 基础知识梳理(3) - 内存区域 中介绍内存区域的时候,对于一个Java对象所占的内存区域是这么介绍的:


在运行过程中,对象头所包含数据的含义不是固定不变的,随着锁状态标志位(下图中红框的范围)的改变,其它字段所表示的含义也不同,以32位的虚拟机为例,下图就是锁状态标志位所对应的数据结构含义:

2.2 Monitor Record

Monitor是线程私有的数据结构,由于一个线程可能进入多个不同的同步方法,这些方法有可能会关联到不同的Monitor,因此每一个线程都有一个可用的Monitor列表,同时还有一个全局的可用列表,Monitor数据结构包括以下成员变量:

三、实现优化

JDK 1.6之后,它对于锁进行了一系列的优化措施,主要包括:自适应自旋锁、锁消除和锁粗化。

3.1 自旋锁

由于线程的阻塞和唤醒需要CPU从用户态转换成核心态,而频繁的阻塞和唤醒对CPU来说是一件负担很重的工作。

因此,我们在发现锁已经被其它线程占有时,并不直接让当前线程进入阻塞状态,而是让线程执行一段无意义的循环,待循环结束后,如何仍然无法获取到锁,那么才进入阻塞状态。

决定自旋锁性能的关键在于自旋次数的选择,在JDK 1.6之后,引入了自适应自旋锁,它会根据前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定新的自旋次数。

3.2 锁消除

JVM检测到不可能存在共享数据竞争,会对同步锁进行锁消除。

3.3 锁粗化

在使用同步锁的时候,需要让同步块的作用范围尽可能地小,仅在共享数据的实际作用域中才进行同步,这样做的目的是为了使需要同步的操作数量尽可能缩小,如果存在锁竞争,那么等待锁的线程也能尽快拿到锁。

然而,如果一系列连续加锁解锁操作,可能会导致不必要的性能损耗,所以有时可以将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁。

四、状态优化

JDK 1.6之前,锁只有两种状态:无锁状态和重量级锁状态,而在这之后增加为四种状态:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,这种改进基于两点考虑:

需要注意,对于锁的这四种状态,它们会随着竞争的激烈而逐渐升级,但是它只允许锁升级,不允许锁降级。

无锁状态和重量级锁状态都比较好理解,下面我们主要介绍新增的两种锁状态:偏向锁状态轻量级锁状态

整个转换的流程图如下所示,在后面的介绍中可以参考:


4.1 偏向锁状态

引入偏向锁的目的是:在无多线程竞争的情况下,尽量减少不必要的轻量级锁执行路径,它的理想情况下是在无竞争时把整个同步都去掉,连CAS操作都省略。

偏向锁的意思是这个锁会偏向于第一个获得它的线程,如果在接下来的执行过程中,该锁没有被其它线程获取,则持有偏向锁的线程将永远不需要再进行同步。

4.1.1 获取偏向锁

(a) 前提条件

获取偏向锁的前提条件是synchronized所修饰的对象处于可偏向状态

(b) 获取过程

当满足前提条件时,再去判断对象的Mark Word中的线程ID是否指向当前线程

4.1.2 释放偏向锁

(a) 前提条件

释放偏向锁的前提条件是其它的线程在竞争偏向锁的过程中出现了失败的情况,并且偏向锁的释放需要等待到达全局安全点。

(b) 释放过程

当满足释放偏向锁的前提条件时,首先会暂停拥有偏向锁的线程,接着判断锁对象是否处于被锁定的状态,决定锁标志位下一步的状态:

4.2 轻量级锁状态

引入轻量级锁的目的是:在无多线程竞争的情况下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。

4.2.1 获取轻量级锁

(a) 前提条件

获取轻量级锁的前提条件时当前对象处于无锁状态,

(b) 获取过程

JVM首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储对象目前的Mark Word的拷贝,之后JVM利用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针:

4.2.2 释放轻量级锁

(a) 释放过程

轻量级锁的释放也是通过CAS操作来进行的:

对于轻量级锁,它性能提升的依据是默认"对于绝大部分的锁,在整个生命周期内是不会存在竞争的",如果不符合这种情况,那么除了互斥的开销外,还有额外的CAS操作,这样轻量级锁比重量级锁更慢。

五、参考文章

Java 并发编程:Synchronized 底层优化(偏向锁、轻量级锁)
死磕 Java 并发 -----深入分析 synchronized 的实现原理

上一篇 下一篇

猜你喜欢

热点阅读