synchronized()_全网最细synchronized讲解补完篇

免责声明: 这是本人第一个原创系列,希望以轻松幽默的风格讲解代码的实现原理,更因为本人水平有限,难免有疏漏的地方。如果读者遇到文章中需要改进或者看不懂,甚至是觉得错误的地方,可以给我留言。

关于synchronized天梯之路的更新结束了,本来想先结束缓一缓的。没办法当初答应了大家的东西就一定要发出去,但是总觉得还缺少了什么,因为我给自己的这个系列定义就是面向高级的,所以特别简单的东西就没有多费笔墨,基本就提一嘴,所以也导致了钻石王者篇内容过于硬核,阅读量少了很多(说的好像,前面两篇阅读量很多一样?),很可能大部分人都不会看完,更别说认认真真看完了,这时候脑子里有一个声音会跟我说,要么做点迎合大部分人口味的科普?不!至少现在的我不愿意做这些别人早就做过的内容,可能随手百度下就能搜到大量相关的博文教程或视频,我就想做别人没做过的,硬核的技术文章,就算我写的东西没有人看,也在互联网上留下了点东西,以后出去面试吹牛也是可以吹上两句的。

系列往期回顾-青铜

系列往期回顾-黄金

系列往期回顾-钻石

系列往期回顾-王者

补充

在说今天的面试题之前,我之前的几篇都没有提到的一个话题就是锁的降级,其实可以搜到外面的很多文章都会说,锁是不会降级的。我的确没有在synchronized的流程中有发现锁降级的相关代码。不过,我实验下来却不是这个结果,在锁对象膨胀成重量级锁之后退出了同步代码块,如果立马打印锁对象头的话,是看到mw仍然是重量级锁的状态,但是当sleep一秒以后,再打印锁对象可以看到就变回了无锁状态。所以我觉得锁是有降级的。下面是demo

public class JOL4Test {

private static Object o = new Object();

private static class T extends Thread {
@Override
public void run() {
synchronized (o) {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}


public static void main(String[] args) throws InterruptedException {
print(o);
new T().start();
synchronized (o) {
System.out.println("In main sync");
print(o);
}
print(o);
Thread.sleep(1000L);
System.out.println("after sleep");
print(o);
}

private static void print(Object object) {
System.out.println(ClassLayout.parseInstance(object).toPrintable());
System.out.println("======================================================");
}
}
4817d3975b98a6e0442661d20a42a9c9.png

回归正题,本期的补完篇就整理下关于synchronized的面试题,今天以下的答案都是我个人认为的正确答案,仅供参考。

1.请描述synchronized和Reentranlock的底层实现及重入的底层原理

这个应该是大部分面试官会问的问题了,首先可以先聊聊两者的相同不同之处。相同:

  • 1.都支持可重入。
  • 2.默认都是非公平锁。
  • 3.底层都用到了CAS,链表

不同:

  • 0.ReentranlockJava实现的,synchronized是关键字由字节码实现。
  • 1.synchronized没有公平锁,而Reentranlock有公平实现。
  • 2.Reentranlock可以创建不同的Condition管理不同情况下的,线程通信。
  • 3.Reentranlock需要显式的释放锁,特别要注意异常情况,synchronized不需要显式的释放锁。

Reentranlock简单的原理

Reentranlock内部使用AQS,而AQS有一个相当重要的state字段,所谓的争抢锁,就是看哪个线程可以把state字段使用CAS的方式,把它从0改到1就视为获取到该锁,而没有获取到锁的线程会进入AQS中的双向链表队列挂起,等待锁释放后的唤醒。

synchronized简单的原理

看了我前面的介绍应该知道synchronized有4种锁:

  • 偏向锁:通过CAS设置当前线程ID至锁对象的mw来获取锁,有线程竞争就会膨胀至重量级锁。
  • 轻量级锁:将mw记录至栈帧中,再使用CAS设置该栈帧指针至锁对象的mw成功便获取锁,有线程竞争会膨胀至重量级锁。
  • 重量级锁:会创建一个Monitor对象并存入mw中,通过CAS设置Monitor对象的onwer字段来获取锁,失败的线程会先进行自旋等待,自旋获取锁失败同样会通过链表的形式进入队列被挂起,等待锁释放后的唤醒。

Reentranlock的重入原理

获得锁的线程通过把state1即可,释放一次减1,减至0视为完全释放。

synchronized的重入原理

  • 偏向锁 | 轻量级锁:通过压一个栈帧的方式去计数重入锁次数,释放锁便弹出一个栈帧,全部弹出后视为完全释放。
  • 重量级锁:使用一个recursions字段去进行计数,原理和Reentranlockstate一样,不再赘述。

2.自旋锁一定比重量级锁效率高吗?

这种题目中有一定的字眼的答案肯定是不一定!但是面试官肯定不是问你这个答案,重要的是背后的理解。看过之前的文章的朋友一定听过我反复强调过,自旋锁本身就是重量级锁的一部分,但是为了避免这里和面试官咬文嚼字,留下不好的印象(除非你就是想和他刚正面),理解到意思就行了,点到为止,点到为止。先把这两种锁解释一下:

  • 1.自旋锁是通过自旋(循环)的方式等待在原地去尝试获取锁。
  • 2.题目中的重量级锁,指的操作系统层面的互斥锁,需要线程从用户态切换到内核态,消耗巨大。

解释完以上的内容,答案就显而易见了,因为自旋锁并不一定能获取到锁,而且会占用CPU的资源,如果自旋到最后还是逃避不了被挂起的命运,简直血亏,还不如上来直接就被挂起来的痛快,所以自旋锁适合同步代码块比较短小的时候,如果同步代码块的业务逻辑特别长,那就不应该自旋直接去挂起更好。

3.打开偏向锁是否效率一定会提升?为什么?

答案同上,不一定!如果有看过钻石篇的朋友们,一定知道偏向锁的逻辑简直比轻量级锁重量级锁加起来都要复杂,而且偏向锁无法应对线程竞争,一旦有竞争就会进行偏向锁撤销,之后再是膨胀流程,如果撤销到一定次数,还会分别进行,批量重偏向批量锁撤销的流程,再加上这些流程都必须等到safepoint才能进行的(原理就不赘述了),所以导致如果频繁的撤销膨胀会消耗巨大性能,所以不如直接关闭偏向锁-XX:-UseBiasedLocking,直接让他膨胀成重量级锁。甚至可以直接使用-XX:+UseHeavyMonitors,只启用重量级锁。

4.请描述锁的四种状态和升级过程

4种状态是:无锁、偏向锁、轻量级锁、重量级锁。如果再细分的话:无锁(无hash)、无锁(有hash)、偏向锁(无锁)、偏向锁(有锁)、轻量级锁、重量级锁。升级过程我在之前的文章中也反复提到了:

  • 无锁 遇到synchronized 升级成 轻量级锁 遇到线程竞争 升级成 重量级锁
  • 偏向锁(无锁) 遇到 synchronized 升级成 偏向锁(有锁) 遇到线程竞争 升级成 重量级锁

如果细分领域的话:

  • 无锁(无hash) 遇到synchronized 升级成 轻量级锁 遇到线程竞争 升级成 重量级锁
  • 无锁(有hash) 遇到synchronized 升级成 轻量级锁 遇到线程竞争 升级成 重量级锁
  • 偏向锁(无锁) 遇到 synchronized 升级成 偏向锁(有锁) 遇到需使用hashcode的方法 升级成 重量级锁。原因很简答,因为偏向锁轻量级锁都没有地方去存放这个临时才决定生成的hashcode,只有重量级锁才能搞定。

5.同时访问synchronized的静态和非静态方法,能保证线程安全吗?

不能,因为锁对象不同,使用的不是同一把锁。

6.父类的方法生命了synchronized,子类重写该方法,但是不声明synchronized,是线程安全的吗?

不是,synchronized不能继承,需要的话,得自己声明。

7.在具体开发中,是使用synchronized修饰方法多,还是代码块多?哪个比较好?

肯定代码块比较好,颗粒度容易控制,而且每一个同步方法,都有一个等效的同步块的写法。

网上搜了些面试题,很多问的都大同小异,我上面基本也都覆盖到了。大家有什么想问的,直接写在评论里,我会回复的。

好了,synchronized系列先告一段落了,以后有什么补充的话,再以精简的短文方式来叙述了。下一个硬核的系列,我大概也已经想好方向了,最近工作比较忙,可能要鸽一段时间了。

最后,再喊出我们的口号:拒绝背书,加油,奥利给!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值