synchronized 在jdk 1.6以前(不包括1.6)是一把很重的锁,每次使用锁的时候都是直接向操作系统请求的,所以效率低,且占资源,所以在jdk1.6以后,加入了锁升级的功能,
锁升级过程
单线程的时候用偏向锁就够了,如果有两个线程了,会升级成轻量级锁(自旋锁、cas),如果线程很多的情况下,就会再次升级为重量级锁;
偏向锁
首先,偏向锁不是一把锁,而是一个标签,一个标识符,特别轻的一把锁,比轻量级锁还轻,轻到什么程度呢?就是无锁;可见这有多轻;偏向锁的概念就是偏向某个线程,使某个线程优先使用;
在大多数情况下,我们运行程序的时候都是只有一个线程的,当我们运行了只有一个线程的synchronized方法之后,这个主线程默认就是偏向锁了;接下来呢我往这个线程里面加个一个同步代码块synchronized ,因为我只有一个线程就没必要再去向系统申请重量级锁了,我就用目前这把偏向锁就够了,因为申请偏向锁会耗费比较大的资源,你得先向操作系统申请:“我要一把锁”,操作系统会判断:“目前有没有可用的锁资源”,有的话就会给你一把;这个会过程比较耗资源;
1、偏向锁升级到轻量级锁的过程 (只要有另一个竞争线程就升级)
偏向锁的时候只有一个线程,当有别的线程进行竞争的时候,首先会在每个线程里都生成自己独立的一个LockRecord(锁记录,简称LR),抢线程的时候使用cas的方式将LR的指针更新到markword里面,成功刷进去了则表示抢到了锁,没抢到的会一直在那自旋。直到抢到锁为止!
2、轻量级锁升级到重量级锁过程
轻量级锁有一个很大的缺点,因为自旋锁是要占用cpu资源的;一个线程在占用锁资源的时候。其他线程就会一直自旋在那等,如果有一万个线程在那等的话。就会有一万个自旋,这得多耗cpu呀,是吧?所以线程数量过多的时候用自旋就不合适了!所以自旋到达一定条件就会升级成重量级锁,升级到重量级锁有以下条件:
- 老版本:2个条件
- 第一个条件,自旋次数超过10次时直接升级为重量级,就是转圈赚了10次后直接升级重量级锁,jvm有个参数 preSpinLock,默认是10次,可以自己设置
- 第二个条件,如果自旋线程的数量超过了cpu的二分之一,也会直接升级为重量级锁;比如你的cpu锁核数是8个,但是有5个线程在那自旋等着占锁使用,8的二分之一是4,5个线程明显已经超过了4,所以这时候直接升级为重量级锁;
- 新版本:Adaptive CAS (自适应自旋),让hotSpot自己决定自旋几次在升级为重量级锁,就相当于自动化了;也就不用我们去管了。所以以后jvm调优就不需要调整自旋次数的参数;因为内部情况怎样只有jvm自己知道;