Java并发编程—锁升级-创新互联
文章目录
————————————————————————————————————创新互联是一家专业提供叙永企业网站建设,专注与网站建设、成都网站制作、H5高端网站建设、小程序制作等业务。10年已为叙永众多企业、政府机构等服务。创新互联专业网站建设公司优惠进行中。锁升级原理
当前文章:Java并发编程—锁升级-创新互联
转载来源:http://scyanting.com/article/ccehsg.html
- 锁升级原理
- 锁的升级的目的
- 锁的四种状态
- 偏向锁
- 轻量级锁
- 重量级锁
- 轻量级锁和重量级锁的对比
- 偏向锁的设置
- 开启偏向锁的方式:
- 自旋锁自旋多少次竞争不到后会升级到重量级锁?
- 为什么有了自旋锁还需要重量级锁?
- 偏向锁是否一定比自旋锁效率高?
————————————————————————————————————创新互联是一家专业提供叙永企业网站建设,专注与网站建设、成都网站制作、H5高端网站建设、小程序制作等业务。10年已为叙永众多企业、政府机构等服务。创新互联专业网站建设公司优惠进行中。锁升级原理
- 锁一开始并不是进入到重量级状态,一开始是在推广阶段是偏向锁,再升级到轻量级锁,再升级到重量级锁。在推广阶段请求较少,用偏向锁就能满足要求
synchronized锁升级原理:
在锁对象的对象头里面有一个threadid字段,在第一次访问的时候threadid为空, jvm 让其持有偏向锁,并将threadid 设置为其线程id,再次进入的时候会先判断。threadid是否与其线程id一致,如果一致则可以直接使用此对象,如果不一致,则升级偏向锁为轻量级锁,通过自旋循环一定次数来获取锁,执行一定次数之后,如果还没有正常获取到要使用的对象,此时就会把锁从轻量级升级为重量级锁!此过程就构成了synchronized锁的升级。
锁的升级的目的:
锁升级是为了减低了锁带来的性能消耗。在Java 6之后优化 synchronized的实现方式,使用了偏向锁升级为轻量级锁再升级到重量级锁的方式,从而减低了锁带来的性能消耗。
上面讲到锁有四种状态,并且会因实际情况进行膨胀升级,其膨胀方向是:
无锁——>偏向锁——>轻量级锁——>重量级锁,并且膨胀方向不可逆。
一句话总结它的作用: 减少统一线程获取锁的代价。在大多数情况下,锁不存在多线程竞争,总是由同一线程多次获得,那么此时就是偏向锁。
- 优点: 加锁和解锁不需要额外的消耗,和执行非同步方法相比仅存在纳秒级别的差距
- 缺点: 如果线程之间存在竞争,会带来额外的锁撤销的消耗
- 使用场景: 适用于一个线程访问同步块的场景
- 核心思想: 如果一个线程获得了锁,那么锁就进入偏向模式,此时Mark word的结构也就变为偏向锁结构,当该线程再次请求锁时,无需再做任何同步操作,即获取锁的过程只需要检查Mark word的锁标记位为偏向锁以及当前线程ID等于Mark word的ThreadID即可,这样就省去了大量有关锁申请的操作。
- 轻量级锁是由偏向锁升级而来,当存在第二个线程申请同一个锁对象时,偏向锁就会立即升级为轻量级锁。
- 注意这里的第二个线程只是申请锁,不存在两个线程同时竞争锁,可以是一前一后地交替执行同步块。
- 自旋: 没有竞争到资源的锁,在极短的时间内查看一次资源
- 好处: 加锁的速度快,能立刻查看到资源被解锁并加上锁
- 缺点: 没竞争到资源的线程也会自旋,会浪费cpu开销,损害cpu利用率
- 使用场景: 自旋锁适合线程较少,少量并发的操作,因为这样浪费的自旋操作少
- 重量级锁是由轻量级锁升级而来,当同一时间有多个线程竞争锁时,锁就会被升级成重量级锁,此时其申请锁带来的开销也就变大。
- 流程: 竞争成功的加锁,竞争失败的进入阻塞队列不参与竞争,cpu全力执行竞争成功的任务,这样cpu利用率比较高;释放锁之后会发一个通知,阻塞队列的线程出来进入就绪队列,再一次进行竞争,竞争失败的再进入阻塞队列。
- 好处: 竞争失败的不会浪费cpu,并发量多的时候,使用重量级锁,cpu浪费率比较低,整体性能更好一些;重量级锁几乎很少导致cpu浪费
- 缺点: 线程阻塞,响应时间慢。
- 使用场景: 适合线程很多,并发很多,追求吞吐量,同步块或者同步方法执行时间较长的场景。
- 轻量级锁竞争的线程不会阻塞,提高程序响应速度;竞争不到线程的锁也会自旋,会消耗cpu;适合追求响应时间快或同步块执行速度快的场景。
- 重量级锁线程竞争失败的会阻塞,不会自旋,不会消耗cpu;线程阻塞后,之前竞争成功的线程在锁释放后需要通知被阻塞的线程,响应时间变慢;适合追求吞吐量或同步块执行速度较长的场景。
—个对象创建时:
- 如果开启了偏向锁(默认开启),那么对象创建后,markword值为0x05即最后3位为101,这时它的thread、epoch、age都为0。
- 偏向锁是默认是延迟的,不会在程序启动时立即生效,如果想避免延迟,可以加VM参数
-xx:BiasedLockingStartupDelay=e
来禁用延迟。 - 如果没有开启偏向锁,那么对象创建后,markword值为0x01即最后3位为001,这时它的 hashcode,age都为0,第一次用到hashcode时才会赋值。
禁用偏向锁:VM 参数 -XX:-UseBiasedLocking
特殊情况:
调用 对象的hashcode方法时,偏向锁也会被禁用. 这是因为调用了对象的 hashCode,但偏向锁的对象 MarkWord 中存储的是线程 id(54位),如果调用 hashCode(31位) 会导致偏向锁被撤销。
撤销 - 其它线程使用对象:
当有其它线程使用偏向锁对象时,会将偏向锁升级为轻量级锁。
批量撤销:
当撤销偏向锁阈值超过 40 次后,jvm 会这样觉得,自己确实偏向错了,根本就不该偏向。于是整个类的所有对象 都会变为不可偏向的,新建的对象也是不可偏向的。
- Java6之前:默认10次或者cpu核数的一半
- Java6之后:根据同一个线程上次自旋的时间决定
自旋是消耗cpu性能的,如果时间过长或者自旋线程过多,cpu会被大量消耗重量级锁中有队列waitSet
偏向锁是否一定比自旋锁效率高?- 单线程的时候偏向锁效率高
- 多个线程的时候偏向锁会涉及锁撤销,消耗资源,这时自旋锁效率高
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
当前文章:Java并发编程—锁升级-创新互联
转载来源:http://scyanting.com/article/ccehsg.html