转载

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 Coder,业余写文章,现主营微信公众号《Java患者》,喜欢的话关注我的公众号或者加我微信我们一起学习Java

synchronized从入门到源码分析
原文  https://juejin.im/post/5df2666bf265da33f63f5015
正文到此结束
Loading...