面试官问我synchronized的底层实现,我差点哭了
每天叫醒你的不是闹钟,而是梦想
微信公众号 : Java患者
Synchronized
synchronized简介
synchronized是一个java的关键字,是java语言为了解决并发编程中存在的原子性、可见性和有序性的问题,提供了一系列跟并发处理有关的关键字,我们今天要来简单了解一下synchronized。
怎么锁?
package com.zero.day3;
/**
* @Description: synchronized
* @Author: Zero
* @Date: 2019/12/10
*/
public class SynchronizedDemo1 {
// 同步方法
public synchronized void wc1 () {
System.out.println("我在上厕所1");
}
// 同步方法 (静态)
public static void wc2 () {
synchronized (SynchronizedDemo1.class) {
System.out.println("我在上厕所2");
}
}
// 同步代码块
public void wc3 () {
synchronized (this) {
System.out.println("我在上厕所3");
}
}
}
上面三个简单的方法中都添加了Synchronized关键字,也就是在同一时间,每个方法只能被单个线程访问,一个厕所同一时间只有有一个人在用。
● 普通同步方法时:锁是当前实例对象
● 静态同步方法时:锁是当前类的class对象(因为静态方法在对象之前运行,运行静态方法的 时候可能都没有对象,所以是当前类的class对象)
● 同步方法块:锁是括号里面的对象
synchronized 实现原理
我们现在对上面的方法进行反编译操作
LINENUMBER 8 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
L1
LOCALVARIABLE this Lcom/zero/day3/SynchronizedDemo1; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x21
public synchronized wc1()V
L0
LINENUMBER 15 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "\u6211\u5728\u4e0a\u5395\u62401"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L1
LINENUMBER 16 L1
RETURN
L2
LOCALVARIABLE this Lcom/zero/day3/SynchronizedDemo1; L0 L2 0
MAXSTACK = 2
MAXLOCALS = 1
// access flags 0x9
public static wc2()V
TRYCATCHBLOCK L0 L1 L2 null
TRYCATCHBLOCK L2 L3 L2 null
L4
LINENUMBER 21 L4
LDC Lcom/zero/day3/SynchronizedDemo1;.class
DUP
ASTORE 0
MONITORENTER
L0
LINENUMBER 22 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "\u6211\u5728\u4e0a\u5395\u62402"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L5
LINENUMBER 23 L5
ALOAD 0
MONITOREXIT
L1
GOTO L6
L2
FRAME FULL [java/lang/Object] [java/lang/Throwable]
ASTORE 1
ALOAD 0
MONITOREXIT
L3
ALOAD 1
ATHROW
L6
LINENUMBER 24 L6
FRAME CHOP 1
RETURN
MAXSTACK = 2
MAXLOCALS = 2
// access flags 0x1
public wc3()V
TRYCATCHBLOCK L0 L1 L2 null
TRYCATCHBLOCK L2 L3 L2 null
L4
LINENUMBER 29 L4
ALOAD 0
DUP
ASTORE 1
MONITORENTER
L0
LINENUMBER 30 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "\u6211\u5728\u4e0a\u5395\u62403"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L5
LINENUMBER 31 L5
ALOAD 1
MONITOREXIT
L1
GOTO L6
L2
FRAME FULL [com/zero/day3/SynchronizedDemo1 java/lang/Object] [java/lang/Throwable]
ASTORE 2
ALOAD 1
MONITOREXIT
L3
ALOAD 2
ATHROW
L6
LINENUMBER 32 L6
FRAME CHOP 1
RETURN
L7
LOCALVARIABLE this Lcom/zero/day3/SynchronizedDemo1; L4 L7 0
MAXSTACK = 2
MAXLOCALS = 3
}
我们仔细找找其实可以找到MONITORENTER跟MONITOREXIT这两个单词,接下来,我就用比较简单粗暴的语言来给大家随便说说这个过程。
假如这个时候呢有三个线程Thread1,Thread2, Thread3都同时来调用一个方法,那么代码肯定是共享的啦,那么当这个方法function没有加锁的时候,Thread1来执行这个代码,执行到一半Thread2也可以来执行,Thread3当然也可以执行。
当这个方法function加了锁之后就不一样了,这代码编译就会产生这两个指令,当Thread1来抢到CPU的执行权之后就来执行这个方法了啊,碰到这个MONITORENTER的时候,这就厉害了啊!其他的线程都给调用了wait方法,大家都知道当线程调用了wait方法之后,就会进入等待状态,谁都救不了它们,这个时候Thread1就自己大摇大摆地执行完了这个加锁的方法,自然就有了MONITOREXIT指令,这个时候线程还在等待?怎么办,当线程处于wait的时候我们都知道会调用notifyAll方法去唤醒所有的其他线程,这个时候所有的线程就苏醒了继续执行。
专注分享Java技术,跟我一起学习吧
长按识别二维码关注
还没有评论,来说两句吧...