synchronized与reentrantLock的性能差距
结论:在JDK 1.6之后,虚拟机对于synchronized关键字进行整体优化后,在性能上synchronized与ReentrantLock已没有明显差距,因此在使用选择上,需要根据场景而定,大部分情况下我们依然建议是synchronized关键字,原因之一是使用方便语义清晰,二是性能上虚拟机已为我们自动优化。而ReentrantLock提供了多样化的同步特性,如超时获取锁、可以被中断获取锁(synchronized的同步是不能中断的)、等待唤醒机制的多个条件变量(Condition)等,因此当我们确实需要使用到这些功能是,可以选择ReentrantLock。参考
两个问题:
- synchronized在1.6之后(包括1.6)到底做了什么优化,提升了性能
- synchronized在优化之前为什么跟lock差异如此之大
synchronized在1.6之后(包括1.6)到底做了什么优化,提升了性能
- 偏向锁:虽然存在竞争的可能,但是实际运行过程中,只有单线程运行
- 轻量级锁:虽然多线程运行,但是线程之间不会存在竞争
- 锁消除:根据逃逸分析等技术,虚拟机断定不会存在多线程的竞争
- 自旋和自适应自旋:频繁的线程上下文、用户态和内核态的切换,性能差,不如自旋等待一会儿消耗点CPU,自旋也不可能太多,自适应自旋最为合理
synchronized在优化之前为什么跟lock差异如此之大
老的synchronized,对于并发的线程,如果都是那种执行比较快,但是并发量又比较高的情况,线程的时间可能主要消耗在系统调用上;老的synchronized只有重量级锁,依赖操作系统底层的mutexlock进行线程的挂起,需要进行系统调用,用户态到内核态的切换,对于性能影响很大。
但是lock是java api层面的锁,内部基于AQS,多线程获取锁的时候,通过cas实现,不需要进行系统调用;cas失败的时候,将线程放入等待队列中,这时候并不是直接将线程挂起,而是判断当前线程是否是队列的头结点,如果是头结点,再次尝试cas获取锁,获取到之后,执行线程;如果线程不在头结点,那么将线程挂起;对于并发较高的小任务类型的多线程场景,能有效的减少线程挂起的次数,减少上下文切换带来的性能损耗。
1 | //尝试获取锁 |