注意在看上面的流程图时需要假设每一步都是当前一个线程在执行,否则会产生混淆
参考:https://www.cnblogs.com/ljl150/p/12514198.html
问题
一、锁对象什么时候处于无锁的状态,即锁标识为01,偏向锁标识为0?
答:
1.当锁对象被创建后,但没有线程进入同步代码块或同步方法(以下称同步代码)时;
2、当线程1获取了偏向锁,线程2尝试获取锁时,线程2会通知线程1暂停,线程1到达了安全点后便会暂停,线程2此时会检查线程1当前是否已经退出同步代码块,如果退出了,线程2便会把锁对象设置为无锁的状态。
二、锁对象是怎么由无锁状态升级为偏向锁状态的?
答:
1、当首次有线程尝试进入同步代码块去获取锁对象时,锁对象由无锁状态升级为偏向锁;
2、当偏向锁被撤销后并降级为无锁状态时,其他线程尝试进入同步代码块去获取锁对象时;
三、线程1获取了锁,把锁对象的状态改成了偏向锁,且在锁对象中存储了自己的线程id,这时线程1执行完了同步代码块,这时它会主动释放锁码?如果不会,那如果线程2此时尝试执行同步代码块时,它获取的是偏向锁还是轻量级锁?
答:不会,线程2获取的是偏向锁,因为当线程1获取了偏向锁,线程2尝试获取锁时,线程2会通知线程1暂停,等线程1到达了安全点后便会暂停,线程2此时会检查线程1当前是否已经退出同步代码块,如果退出了,线程2便会把锁对象设置为无锁的状态,接着唤醒线程1从安全点继续执行,并再去重新尝试获取锁。
三、锁对象是什么情况下由偏向锁状态升级为轻量级锁?
答:当存在线程竞争锁时。
问:是否能描述下由偏向锁升级到轻量级锁的过程?
答:当线程1获取了偏向锁,线程2尝试获取锁时,线程2会通知线程1暂停,等线程1到达了安全点后便会暂停,此时线程2会检查线程1当前是否已经退出同步代码块,如果退出了,线程2便会把锁对象设置为无锁的状态,接着唤醒线程1,并再去重新尝试获取锁;如果没有退出,那么锁对象便升级为轻量级锁。
四、轻量级锁的加锁过程
- 在代码进入同步块的时候,如果同步对象锁状态为无锁状态(锁标志位为“01”状态,是否为偏向锁为“0”),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝,官方称之为 Displaced Mark Word。这时候线程堆栈与对象头的状态如图:
- 拷贝对象头中的Mark Word复制到锁记录(Lock Record)中;
- 拷贝成功后,虚拟机将使用CAS操作尝试将锁对象的Mark Word更新为指向Lock Record的指针,并将线程栈帧中的Lock Record里的owner指针指向Object的 Mark Word。
- 如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位设置为“00”,即表示此对象处于轻量级锁定状态,这时候线程堆栈与对象头的状态如图所示。
- 如果这个更新操作失败了,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行。否则说明多个线程竞争锁,轻量级锁就要膨胀为重量级锁,锁标志的状态值变为“10”,Mark Word中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也要进入阻塞状态。
- 轻量级锁认为竞争存在,但是竞争的程度很轻,一般两个线程对于同一个锁的操作都会错开,或者说稍微等待一下(自旋),另一个线程就会释放锁。 但是当自旋超过一定的次数,或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁膨胀为重量级锁,重量级锁使除了拥有锁的线程以外的线程都阻塞,防止CPU空转。
synchronized 是使用了操作系统的mutex实现阻塞,LockSuppor.park也是吗?
答:事实上,synchronized和LockSuppor.park还有Object.wait在JVM源码层使用的是同一个函数,请移步https://zhuanlan.zhihu.com/p/309822935