博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JVM 对 Java 的原生锁做了哪些优化?
阅读量:4216 次
发布时间:2019-05-26

本文共 1153 字,大约阅读时间需要 3 分钟。

自旋锁

在 Java 6 之前,Monitor 的实现完全依赖底层操作系统的互斥锁来实现。

由于 Java 层面的线程与操作系统的原生线程有映射关系,如果要将一个线程进行阻塞或唤起都需要操作系统的协助,这就需要从用户态切换到内核态来执行,这种切换代价十分昂贵,很耗处理器时间,现代 JDK 中做了大量的优化。
一种优化是使用自旋锁,即在把线程进行阻塞操作之前先让线程自旋等待一段时间,可能在等待期间其他线程已经解锁,这时就无需再让线程执行阻塞操作,避免了用户态到内核态的切换。

Java 虚拟机的开发工程师们在分析过大量数据后发现:共享数据的锁定状态一般只会持续很短的一段时间,为了这段时间去挂起和恢复线程其实并不值得。

自旋锁在 JDK 1.4 中引入,在 JDK 1.6 中默认开启。

自旋等待虽然避免了线程切换的开销,但自旋的线程要占用处理器时间的,所以若锁被占用的时间很短,自旋等待的效果就会非常好,反之锁被占用的时间很长,那么自旋的线程只会白白消耗 CPU 资源。

因此自旋等待的时间必须要有一定的限度,超过限定的次数仍然没有成功获得锁,就应当挂起(阻塞)线程了。自旋次数的默认值是 10 次。

自适应自旋

在 JDK 1.6 中引入了自适应自旋锁。

自适应意味着自旋的时间不再固定了,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。

如果在同一个锁对象上,自旋等待刚刚成功获得过锁,并且持有锁的线程正在运行中,那么虚拟机就会认为这次自旋也很有可能再次成功,进而它将允许自旋等待持续相对更长的时间,比如100个循环。

如果对于某个锁,自旋很少成功获得过,那在以后要获取这个锁时将可能省略掉自旋过程,以避免浪费处理器资源。

锁消除

在动态编译同步块的时候,JIT 编译器可以借助一种被称为逃逸分析(Escape Analysis)的技术来判断同步块所使用的锁对象是否只能够被一个线程访问而没有被发布到其他线程。从而取消对这部分代码的同步。

锁消除:指虚拟机即时编译器在运行时,对一些代码上要求同步,但被检测到不可能存在共享数据竞争的锁进行消除。主要根据逃逸分析。

程序员怎么会在明知道不存在数据竞争的情况下使用同步呢?很多不是程序员自己加入的。

锁粗化

当 JIT 编译器发现一系列连续的操作都对同一个对象反复加锁和解锁,甚至加锁操作出现在循环体中的时候,会将加锁同步的范围扩散(粗化)到整个操作序列的外部。

在编写代码的时候,总是推荐将同步块的作用范围(锁粒度)限制得尽量小(只在共享数据的实际作用域中才进行同步),这样是为了使得需要同步的操作数量尽可能变小,如果存在锁竞争,那等待锁的线程可以尽快的拿到锁。 

锁粒度:不要锁住一些无关的代码。锁粗化:可以一次执行完的不要多次加锁执行

 

 

 

 

 

 

转载地址:http://tatmi.baihongyu.com/

你可能感兴趣的文章
深度学习知识点整理
查看>>
Logistic Regression知识点整理
查看>>
Boosting、Bagging和Stacking知识点整理
查看>>
EM算法知识点整理
查看>>
优化算法知识点整理
查看>>
缺失值处理知识点整理
查看>>
剑指Offer题解(Python版)
查看>>
Leetcode 链表知识点总结
查看>>
Leetcode 位运算知识点总结
查看>>
性能度量(模型评价)
查看>>
Leetcode 递归知识点总结
查看>>
Leetcode 栈知识点总结
查看>>
算法专题训练(1)股票问题
查看>>
Leetcode 动态规划知识点总结
查看>>
算法专题训练(2)小偷问题
查看>>
八大排序算法
查看>>
算法专题训练(3)回文字符串
查看>>
机器学习面试题整理
查看>>
Leetcode Math知识点总结
查看>>
集成学习专题之GBDT面试
查看>>