面试官问我synchronized的底层实现,我差点哭了

不念不忘少年蓝@ 2023-02-21 11:45 81阅读 0赞

format_png

每天叫醒你的不是闹钟,而是梦想

微信公众号 : Java患者

Synchronized

synchronized简介

synchronized是一个java的关键字,是java语言为了解决并发编程中存在的原子性、可见性和有序性的问题,提供了一系列跟并发处理有关的关键字,我们今天要来简单了解一下synchronized。

怎么锁?

  1. package com.zero.day3;
  2. /**
  3. * @Description: synchronized
  4. * @Author: Zero
  5. * @Date: 2019/12/10
  6. */
  7. public class SynchronizedDemo1 {
  8. // 同步方法
  9. public synchronized void wc1 () {
  10. System.out.println("我在上厕所1");
  11. }
  12. // 同步方法 (静态)
  13. public static void wc2 () {
  14. synchronized (SynchronizedDemo1.class) {
  15. System.out.println("我在上厕所2");
  16. }
  17. }
  18. // 同步代码块
  19. public void wc3 () {
  20. synchronized (this) {
  21. System.out.println("我在上厕所3");
  22. }
  23. }
  24. }

上面三个简单的方法中都添加了Synchronized关键字,也就是在同一时间,每个方法只能被单个线程访问,一个厕所同一时间只有有一个人在用。

● 普通同步方法时:锁是当前实例对象
● 静态同步方法时:锁是当前类的class对象(因为静态方法在对象之前运行,运行静态方法的 时候可能都没有对象,所以是当前类的class对象)
● 同步方法块:锁是括号里面的对象

synchronized 实现原理

我们现在对上面的方法进行反编译操作

  1. LINENUMBER 8 L0
  2. ALOAD 0
  3. INVOKESPECIAL java/lang/Object.<init> ()V
  4. RETURN
  5. L1
  6. LOCALVARIABLE this Lcom/zero/day3/SynchronizedDemo1; L0 L1 0
  7. MAXSTACK = 1
  8. MAXLOCALS = 1
  9. // access flags 0x21
  10. public synchronized wc1()V
  11. L0
  12. LINENUMBER 15 L0
  13. GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  14. LDC "\u6211\u5728\u4e0a\u5395\u62401"
  15. INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
  16. L1
  17. LINENUMBER 16 L1
  18. RETURN
  19. L2
  20. LOCALVARIABLE this Lcom/zero/day3/SynchronizedDemo1; L0 L2 0
  21. MAXSTACK = 2
  22. MAXLOCALS = 1
  23. // access flags 0x9
  24. public static wc2()V
  25. TRYCATCHBLOCK L0 L1 L2 null
  26. TRYCATCHBLOCK L2 L3 L2 null
  27. L4
  28. LINENUMBER 21 L4
  29. LDC Lcom/zero/day3/SynchronizedDemo1;.class
  30. DUP
  31. ASTORE 0
  32. MONITORENTER
  33. L0
  34. LINENUMBER 22 L0
  35. GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  36. LDC "\u6211\u5728\u4e0a\u5395\u62402"
  37. INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
  38. L5
  39. LINENUMBER 23 L5
  40. ALOAD 0
  41. MONITOREXIT
  42. L1
  43. GOTO L6
  44. L2
  45. FRAME FULL [java/lang/Object] [java/lang/Throwable]
  46. ASTORE 1
  47. ALOAD 0
  48. MONITOREXIT
  49. L3
  50. ALOAD 1
  51. ATHROW
  52. L6
  53. LINENUMBER 24 L6
  54. FRAME CHOP 1
  55. RETURN
  56. MAXSTACK = 2
  57. MAXLOCALS = 2
  58. // access flags 0x1
  59. public wc3()V
  60. TRYCATCHBLOCK L0 L1 L2 null
  61. TRYCATCHBLOCK L2 L3 L2 null
  62. L4
  63. LINENUMBER 29 L4
  64. ALOAD 0
  65. DUP
  66. ASTORE 1
  67. MONITORENTER
  68. L0
  69. LINENUMBER 30 L0
  70. GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  71. LDC "\u6211\u5728\u4e0a\u5395\u62403"
  72. INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
  73. L5
  74. LINENUMBER 31 L5
  75. ALOAD 1
  76. MONITOREXIT
  77. L1
  78. GOTO L6
  79. L2
  80. FRAME FULL [com/zero/day3/SynchronizedDemo1 java/lang/Object] [java/lang/Throwable]
  81. ASTORE 2
  82. ALOAD 1
  83. MONITOREXIT
  84. L3
  85. ALOAD 2
  86. ATHROW
  87. L6
  88. LINENUMBER 32 L6
  89. FRAME CHOP 1
  90. RETURN
  91. L7
  92. LOCALVARIABLE this Lcom/zero/day3/SynchronizedDemo1; L4 L7 0
  93. MAXSTACK = 2
  94. MAXLOCALS = 3
  95. }

我们仔细找找其实可以找到MONITORENTER跟MONITOREXIT这两个单词,接下来,我就用比较简单粗暴的语言来给大家随便说说这个过程。

  1. 假如这个时候呢有三个线程Thread1Thread2, Thread3都同时来调用一个方法,那么代码肯定是共享的啦,那么当这个方法function没有加锁的时候,Thread1来执行这个代码,执行到一半Thread2也可以来执行,Thread3当然也可以执行。
  2. 当这个方法function加了锁之后就不一样了,这代码编译就会产生这两个指令,当Thread1来抢到CPU的执行权之后就来执行这个方法了啊,碰到这个MONITORENTER的时候,这就厉害了啊!其他的线程都给调用了wait方法,大家都知道当线程调用了wait方法之后,就会进入等待状态,谁都救不了它们,这个时候Thread1就自己大摇大摆地执行完了这个加锁的方法,自然就有了MONITOREXIT指令,这个时候线程还在等待?怎么办,当线程处于wait的时候我们都知道会调用notifyAll方法去唤醒所有的其他线程,这个时候所有的线程就苏醒了继续执行。

专注分享Java技术,跟我一起学习吧

长按识别二维码关注

format_png 1

format_png 2

发表评论

表情:
评论列表 (有 0 条评论,81人围观)

还没有评论,来说两句吧...

相关阅读

    相关 腾讯面试用「B+树」虐

    > 我们知道当系统要处理的数据量非常庞大的时候,数据不可能全部存放于内存,需要借助磁盘来完成存储和检索。在数据库中支持很多种索引方式,常见有哈希索引、全文索引和B+树索引。今天