刚写了篇偏向锁和轻量级锁的文章(https://www.jianshu.com/p/31b6a0b1b84b),虽然大部分是根据网上资料整理的,但是自己也算是有丶认识了,写一个自我面试试试,看看能想起多少。

interviewer:小伙子,说说Java中有那些锁?

me:自旋锁、偏向锁、轻量级锁、重量级锁、乐观锁、悲观锁、可重入锁、读写锁、公平锁、非公平锁。。。

interviewer: 停停停,兄弟,你是来捣乱的么?

me (in heart):我是来砸场子的!!!

interviewer:那你说说JDK关于synchronized的优化吧

me:好的,您坐稳了。

me: synchronized在优化之前,当对一个临界区资源上锁后,其他竞争临界区且未获得锁的线程都会进入阻塞状态,当第一个线程执行完释放锁后,会唤醒这些线程,进入新的竞争状态。这样做会导致频繁的进行状态转换,效率比较低。因此在JDK1.6后,对synchronized关键字进行了优化,使它变得不是那么“重量级锁”了。

me:在JDK1.6中,锁共有4种状态:“无锁状态”,“偏向锁”,“轻量级锁”,“重量级锁”。这些状态保存在加锁的对象头的MarkWord中。而这个加锁的对象有三种情况:对于普通方法来说,就是当前实例对象,对于静态方法来说,就是当前的类对象,对于代码块来说,就是synchronized关键字括号里的对象。

me:那我接下来说一下偏向锁吧,偏向锁会偏向第一个访问它的线程,当一个线程第一次访问同步块时,会把这个对象的头部代表线程ID的位修改为此线程ID,而且运行完之后不会主动释放偏向锁,所以次线程以后在进入和退出同步块的时候不需要进行CAS操作加锁解锁,提高了效率。

me:线程访问同步块时,它会看下当前的线程ID是否和对象头的偏向线程ID一致,如果一致,说明已经获得了偏向锁,直接运行同步块。如果不一致,则尝试使用CAS将对象头的偏向线程改成当前ID。

me:这个时候出现了竞争,所以偏向锁会释放,等到全局安全点的时候,先暂停原来持有偏向锁的线程,如果线程运行完了同步块,则把对象变成无锁状态,如果还在同步块中,则偏向锁升级,变成轻量级锁,修改相应的对象头标志位。唤醒暂停的线程。

me:接下来说下轻量级锁吧,线程在执行同步块之前会在线程的虚拟机栈中创建一个存储锁记录的空间,并把对象头的Mark Word复制过去,然后通过CAS把对象头变成指向虚拟机栈中记录的指针,如果成功,则获得锁,如果失败,则自旋获取锁。

me:线程运行完同步块后,会释放轻量级锁,此时使用CAS去把虚拟机栈中的记录替换回对象头,如果成功,就释放成功了,如果失败,说明有其他线程修改了mark word,此时就会膨胀为重量级锁。