一、引言
关于synchronized关键字,以前经常听说过两句话:synchronized是重量级锁;jdk1.6以后synchronized性能有较大的提升。那么,为什么称synchronized为重量级锁呢?jdk1.6又做了什么优化使得synchronized有很大的性能提升呢?
本文把之前的学习的synchronized锁优化整理下来。
二、为什么称为重量级锁?
我们都知道Object内部维护一个监视器Monitor,synchronized正是通过监视器锁实现同步的。监视器锁本质又是依赖于底层的操作系统的Mutex Lock(互斥锁)来实现的。每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
互斥锁:用于保护临界区,确保同一时间只有一个线程访问数据。对共享资源的访问,先对互斥量进行加锁,如果互斥量已经上锁,调用线程会阻塞,直到互斥量被解锁。在完成了对共享资源的访问后,要对互斥量进行解锁。
mutex的工作方式:1) 申请mutex
2) 如果成功,则持有该mutex3) 如果失败,则进行spin自旋.(不断发起mutex gets, 直到获得mutex或者达到spin_count限制为止)4) 依据工作模式的不同选择yiled还是sleep5) 若达到sleep限制或者被主动唤醒或者完成yield, 则重复1)~4)步,直到获得为止由于Java的线程是映射到操作系统的原生线程之上的,如果要阻塞或唤醒一条线程,都需要操作系统来帮忙完成,这就需要从用户态转换到核心态中,因此状态转换需要耗费很多的处理器时间。
三、锁优化
自旋锁
通常情况下共享数据的锁定状态只会持续很短的一段时间,为了这段时间去挂起和恢复线程并不值得。可以让正在请求锁的那个线程执行一个忙循环,不会放弃处理器的时间片,看看持有锁的线程是否很快就会释放锁。这就是自旋锁的基本原理。
自旋等待虽然避免了线程切换的开销,但它是要占用处理器时间的,所以如果锁被占用的时间很短,自旋等待的效果就会非常好,反之如果锁被占用的时间很长,那么自旋的线程反而会消耗处理器资源。如果自旋超过了限定的次数仍然没有成功获得锁,就应当挂起线程做切换。
自适应自旋
在JDK 1.6中引入了自适应的自旋锁。如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也很有可能再次成功,进而它将允许自旋等待持续相对更长的时间。反之,如果对于某个锁,自旋很少成功获得过,那在以后要获取这个锁时将可能省略掉自旋过程,以避免浪费处理器资源。随着程序运行和性能监控信息的不断完善,虚拟机对程序锁的状况预测就会越来越准确。
锁消除
锁削除是指虚拟机即时编译器在运行时,对检测到的不可能存在共享数据竞争的锁进行削除。锁削除的主要判定依据来源于逃逸分析,如果判断到一段代码中,在堆上的所有数据都不会逃逸出去被其他线程访问到,那就不需要同步加锁。
有些时候,虽然没有显示使用锁,但是一些JDK的内置API或第三方jar包会存在隐藏的加锁动作,如StringBuffer的append()方法、Vector的add()方法。
锁粗化
如果一系列的连续操作都对同一个对象反复加锁和解锁,或者加锁操作是出现在循环体内,那即使没有出现线程竞争,频繁地进行互斥同步操作也会导致不必要的性能损耗。如果虚拟机检测到有一串零碎的操作都是对同一对象的加锁,将会把加锁同步的范围扩大到覆盖这些操作。
轻量级锁
轻量级锁的主要作用是在无实际竞争的情况下,减少传统的重量级锁的互斥消耗。当关闭偏向锁或者多个线程竞争偏向锁时,则会获取轻量级锁。
对象头部负责维护锁的状态,不同阶段的锁对应不同的状态,32位系统的格式如下:
轻量级锁的执行过程如下:
获取锁: 1. 判断当前对象是否处于无锁状态(01),若是,则JVM在当前线程的栈帧中建立一个Lock Record空间,用于存储锁对象当前的头部运行数据的拷贝;否则执行步骤(3);释放锁:
1. 取出在获取轻量级锁保存在对象的头部运行数据; 2. 执行CAS替换当前对象的头部运行数据,如果成功,则说明释放锁成功,否则执行(3); 3. 如果CAS替换失败,说明有其他线程尝试获取该锁,则在释放锁时唤醒被挂起的线程。偏向锁
轻量级锁是在无实际竞争的情况下使用CAS操作去消除互斥的性能消耗,偏向锁的目的,是在轻量级锁的前提下进一步提高程序的运行性能,减去CAS操作的消耗。 可以理解为锁会偏向于第一个获得它的线程,如果在接下来的执行过程中,该锁没有被其他的线程获取,则持有偏向锁的线程将永远不需要再进行同步。
当有另外一个线程去尝试获取这个锁时,偏向模式就宣告结束。根据锁对象目前是否处于被锁定的状态,撤销偏向锁后恢复到未锁定(标志位为“01”)或轻量级锁定(标志位为“00”)状态。
四种锁的状态转变: 无锁-->偏向锁-->轻量级锁-->重量级锁