目录
什么是锁升级
使用Synchronized会让其他线程阻塞,这样很影响程序的总体效率。因此jdk1.6以后优化了Synchronized机制即——锁升级
锁升级的过程为:无锁=》偏向锁=》轻量级锁=》重量级锁
注意:锁是不会降级的!
什么是锁消除
假如你在一个方法中用到了锁,但JVM并没有检查到有共享数据竞争的问题,为了减少性能损耗,JVM会把你的这个锁消除掉
什么是锁粗化
让锁的作用范围尽可能的小,这样可以让锁内代码尽可能少,缩短锁的持有时间。
但锁粗化并不都是有益的。比如有一个for循环,你频繁在for循环里面上锁,释放锁非常影响性能
什么是Mark Word
Mark Word用来存储对象运行时数据,用来记录对象运行时的一些参数,如:GC年龄,HashCode,锁标志,栈中的指针等
锁升级的过程
1.当没有对象上锁,对象为普通的对象。锁标志位为01,偏向锁标志为0
2.当对象被当作同步锁(Synchronized),假如线程A抢到了。锁标志依然为01,偏向锁标志变为1,此时锁升级为偏向锁。
偏向锁意味着如果再次进入或者退出同一段同步代码块,并不在需要进行加锁或解锁
3.当线程A再次试图获取锁时,JVM发现同步锁对象标志位是01,偏向锁标志为1。MarkWord会记录线程id就是A的id,表示A可以执行同步代码块。
4.当线程B尝试获取锁,JVM发现已经有人占用了,并且线程ID不是B的ID,因此B会先用CAS尝试比较并交换,成功则B获得锁,失败B会进入自旋。
5.B失败后说明锁存在竞争,这样就会导致”偏向锁升级为轻量级锁“。B会再次和A进行CAS比较并交换
6.如果轻量级锁后B仍然抢锁失败,就会使用自旋锁循环,自旋次数由JVM决定。如果某一时间成功,B获得锁。
7.如果B仍然抢锁失败,轻量级锁会升级为重量级锁,标志位改成10,在这个状态下未抢到锁的线程都会被阻塞,并由操作系统底层的Monitor来管理,这样就会出现CPU用户态和内核态的切换,会消耗资源——又名”重量级锁“
一个对象刚开始实例化的时候,没有任何线程来访问它。它是可偏向的,意味着它现在认为只可能有一个线程访问它,因此第一个线程访问它之后会偏向第一个线程,这个线程在执行偏向锁操作时使用了CAS,将对象头的ThreadID改成自己的ID,之后再次访问这个对象只需要比较ID,而不需要再使用CAS进行操作。
一旦有第二个线程访问这个对象,因为偏向锁不会主动释放,所以第二个线程可以看到对象是偏向锁状态。此时该对象已经存在锁竞争了,会检查原来持有该对象锁的线程是否依然存活,如果挂了则可改变成无锁状态,然后重新偏向新的线程,如果原来的线程依然存活,则马上执行那个线程的操作栈,如果仍然持有偏向锁,会被升级成轻量级锁,如果不存在使用了,将对象恢复到无锁的状态,然后重新偏向。
轻量级锁认为竞争存在,但是竞争的程度很轻,一般两个线程对于同一个锁的操作都会错开(自旋),当自旋超过一定次数,或一个线程持有锁,一个线程在自旋,这时来了第三个线程,轻量级锁会膨胀为重量级锁,重量级锁使除了拥有锁的线程外的其他线程全部阻塞,防止CPU空转