Java之Synchronized关键字
2022-06-29 14:53:17 12 举报
AI智能生成
Java之Synchronized关键字
作者其他创作
大纲/内容
作用对象
普通同步方法,锁是当前实例对象
静态同步方法,锁是当前类的class对象
同步方法块,锁是括号里面的对象
底层原理
基于进入和退出Monitor对象实现
对象头
Mark Word(标记字段)、Klass Pointer(类型指针)
实现轻量级锁和偏向锁的关键
锁的优化
jdk1.6之前
底层monitor会阻塞和唤醒线程,线程的阻塞和唤醒需要CPU从“用户态”转为“内核态”
1.6之后
无锁
底层是通过CAS实现的
偏向锁
在大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低,引进了偏向锁
这个锁会偏向于第一个获得它的线程,会在对象头存储锁偏向的线程ID
以后该线程进入和退出同步块时只需要检查是否为偏向锁、锁标志位以及ThreadID即可
原理
1. 虚拟机将会把对象头中的标志位设为“01”,即偏向模式。
2. 同时使用CAS操作把获取到这个锁的线程的ID记录在对象的Mark Word之中 ,
如果CAS操作成功,持有偏向锁的线程以后每次进入这个锁相关的同步块时,虚拟机都可以不再进行任何同步操作,偏向锁的效率高。
2. 同时使用CAS操作把获取到这个锁的线程的ID记录在对象的Mark Word之中 ,
如果CAS操作成功,持有偏向锁的线程以后每次进入这个锁相关的同步块时,虚拟机都可以不再进行任何同步操作,偏向锁的效率高。
偏向锁的撤销
1. 偏向锁的撤销动作必须等待全局安全点
2. 暂停拥有偏向锁的线程,判断锁对象是否处于被锁定状态
3. 撤销偏向锁,恢复到无锁(标志位为 01)或轻量级锁(标志位为 00)的状态
指令
偏向锁在Java 6之后是默认启用的,但在应用程序启动几秒钟(4s)之后才激活,可以使用-XX:BiasedLockingStartupDelay=0 参数关闭延迟,
如果确定应用程序中所有锁通常情况下处于竞争状态,可以通过XX:-UseBiasedLocking=false 参数关闭偏向锁。
如果确定应用程序中所有锁通常情况下处于竞争状态,可以通过XX:-UseBiasedLocking=false 参数关闭偏向锁。
轻量级锁
目的
在多线程交替执行同步块的情况下,尽量避免重量级锁引起的性能消耗,但是如果多个线程在同一时刻进入临界区,会导致轻量级锁膨胀升级重量级锁,所以轻量级锁的出现并非是要替代重量级锁
原理
当关闭偏向锁功能或者多个线程竞争偏向锁导致偏向锁升级为轻量级锁,则会尝试获取轻量级锁
将对象的Mark Word复制到栈帧中的Lock Recod中。Mark Word更新为指向Lock Record的指针。
好处
在多线程交替执行同步块的情况下,可以避免重量级锁引起的性能消耗。
释放
CAS操作
1. 取出在获取轻量级锁保存在Displaced Mark Word中的数据。
2. 用CAS操作将取出的数据替换当前对象的Mark Word中,如果成功,则说明释放锁成功。
3. 如果CAS操作替换失败,说明有其他线程尝试获取该锁,则需要将轻量级锁需要膨胀升级为重量级锁。
2. 用CAS操作将取出的数据替换当前对象的Mark Word中,如果成功,则说明释放锁成功。
3. 如果CAS操作替换失败,说明有其他线程尝试获取该锁,则需要将轻量级锁需要膨胀升级为重量级锁。
对于轻量级锁,其性能提升的依据是“对于绝大部分的锁,在整个生命周期内都是不会存在竞争的”,如果打破这个依据则除了互斥的开销外,
还有额外的CAS操作,因此在有多线程竞争的情况下,轻量级锁比重量级锁更慢。
还有额外的CAS操作,因此在有多线程竞争的情况下,轻量级锁比重量级锁更慢。
自旋锁
自旋锁原理
monitor实现锁的时候,知道monitor会阻塞和唤醒线程,线程的阻塞和唤醒需要CPU从用户态转为内核态,频繁的阻塞和唤醒对CPU来说是一件负担很重的工作
自旋等待不能代替阻塞,且先不说对处理器数量的要求,自旋等待本身虽然避免了线程切换的开销,但它是要占用处理器时间的
如果锁被占用的时间很短,自旋等待的效果就会非常好,
如果锁被占用的时间很长。那么自旋的线程只会白白消耗处理器资源,而不会做任何有用的工作,反而会带来性能上的浪费。
如果锁被占用的时间很长。那么自旋的线程只会白白消耗处理器资源,而不会做任何有用的工作,反而会带来性能上的浪费。
自旋等待的时间必须要有一定的限度,如果自旋超过了限定的次数仍然没有成功获得锁,就应当使用传统的方式去挂起线程
自旋次数的默认值是10次,用户可以使用参数-XX : PreBlockSpin来更改
自旋锁在JDK 1.4.2中就已经引入 ,只不过默认是关闭的,可以使用-XX:+UseSpinning参数来开启,在JDK 6中 就已经改为默认开启了
适应性自旋锁
JDK 6中引入了自适应的自旋锁。自适应意味着自旋的时间不再固定了,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定
如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也很有可能再次成功,
进而它将允许自旋等待持续相对更长的时间,比如100次循环。
进而它将允许自旋等待持续相对更长的时间,比如100次循环。
如果对于某个锁,自旋很少成功获得过,那在以后要获取这个锁时将可能省略掉自旋过程,以避免浪费处理器资源
锁消除
指虚拟机即时编译器(JIT)在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除
判定依据
来源于逃逸分析的数据支持,如果判断在一段代码中,堆上的所有数据都不会逃逸出去从而被其他线程访问到,那就可以把它们当做栈上数据对待,认为它们是线程私有的,同步加锁自然就无须进行
锁粗化
0 条评论
下一页