前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >被问到傻傻不懂synchronized底层原理

被问到傻傻不懂synchronized底层原理

作者头像
BUG弄潮儿
发布2020-06-15 16:56:14
3400
发布2020-06-15 16:56:14
举报
文章被收录于专栏:JAVA乐园

0x01:synchronized的基本语法

修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

0x02:代码分析synchronized

代码语言:javascript
复制
package com.lesson8;

public class SynchronizedDemo {

    public Object lock = new Object();

    public synchronized void  syncCommonMethod(){
        System.out.println("==syncCommonMethod==");
    }

    public static synchronized void  syncStaticMethod(){
        System.out.println("==syncStaticMethod==");
    }

    public void  syncBlockCode(){
        synchronized (lock) {
            System.out.println("==syncBlockCode==");
        }
    }

}

代码中包三个方法,分别是synchronized修饰实例方法、synchronized静态方法synchronized修饰代码块。使用命令:

javap -v SynchronizedDemo.class

得到如下汇编指令:

代码语言:javascript
复制
Classfile /D:/jmeterws/xml/com-lesson8/target/classes/com/lesson8/SynchronizedDemo.class
  Last modified 2020-4-5; size 926 bytes
  MD5 checksum f23cff99c70b8a401db1cc3bd74538ec
  Compiled from "SynchronizedDemo.java"
public class com.lesson8.SynchronizedDemo
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Class              #2             // com/lesson8/SynchronizedDemo
   #2 = Utf8               com/lesson8/SynchronizedDemo
   #3 = Class              #4             // java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               lock
   #6 = Utf8               Ljava/lang/Object;
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Methodref          #3.#11         // java/lang/Object."<init>":()V
  #11 = NameAndType        #7:#8          // "<init>":()V
  #12 = Fieldref           #1.#13         // com/lesson8/SynchronizedDemo.lock:Ljava/lang/Object;
  #13 = NameAndType        #5:#6          // lock:Ljava/lang/Object;
  #14 = Utf8               LineNumberTable
  #15 = Utf8               LocalVariableTable
  #16 = Utf8               this
  #17 = Utf8               Lcom/lesson8/SynchronizedDemo;
  #18 = Utf8               syncCommonMethod
  #19 = Fieldref           #20.#22        // java/lang/System.out:Ljava/io/PrintStream;
  #20 = Class              #21            // java/lang/System
  #21 = Utf8               java/lang/System
  #22 = NameAndType        #23:#24        // out:Ljava/io/PrintStream;
  #23 = Utf8               out
  #24 = Utf8               Ljava/io/PrintStream;
  #25 = String             #26            // ==syncCommonMethod==
  #26 = Utf8               ==syncCommonMethod==
  #27 = Methodref          #28.#30        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #28 = Class              #29            // java/io/PrintStream
  #29 = Utf8               java/io/PrintStream
  #30 = NameAndType        #31:#32        // println:(Ljava/lang/String;)V
  #31 = Utf8               println
  #32 = Utf8               (Ljava/lang/String;)V
  #33 = Utf8               syncStaticMethod
  #34 = String             #35            // ==syncStaticMethod==
  #35 = Utf8               ==syncStaticMethod==
  #36 = Utf8               syncBlockCode
  #37 = String             #38            // ==syncBlockCode==
  #38 = Utf8               ==syncBlockCode==
  #39 = Utf8               StackMapTable
  #40 = Class              #41            // java/lang/Throwable
  #41 = Utf8               java/lang/Throwable
  #42 = Utf8               SourceFile
  #43 = Utf8               SynchronizedDemo.java
{
  public java.lang.Object lock;
    descriptor: Ljava/lang/Object;
    flags: ACC_PUBLIC

  public com.lesson8.SynchronizedDemo();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: invokespecial #10                 // Method java/lang/Object."<init>":()V
         4: aload_0
         5: new           #3                  // class java/lang/Object
         8: dup
         9: invokespecial #10                 // Method java/lang/Object."<init>":()V
        12: putfield      #12                 // Field lock:Ljava/lang/Object;
        15: return
      LineNumberTable:
        line 3: 0
        line 5: 4
        line 3: 15
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      16     0  this   Lcom/lesson8/SynchronizedDemo;

  public synchronized void syncCommonMethod();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #19                 // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #25                 // String ==syncCommonMethod==
         5: invokevirtual #27                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 8: 0
        line 9: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Lcom/lesson8/SynchronizedDemo;

  public static synchronized void syncStaticMethod();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=0, args_size=0
         0: getstatic     #19                 // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #34                 // String ==syncStaticMethod==
         5: invokevirtual #27                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 12: 0
        line 13: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature

  public void syncBlockCode();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=1
         0: aload_0
         1: getfield      #12                 // Field lock:Ljava/lang/Object;
         4: dup
         5: astore_1
         6: monitorenter
         7: getstatic     #19                 // Field java/lang/System.out:Ljava/io/PrintStream;
        10: ldc           #37                 // String ==syncBlockCode==
        12: invokevirtual #27                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        15: aload_1
        16: monitorexit
        17: goto          23
        20: aload_1
        21: monitorexit
        22: athrow
        23: return
      Exception table:
         from    to  target type
             7    17    20   any
            20    22    20   any
      LineNumberTable:
        line 16: 0
        line 17: 7
        line 16: 15
        line 19: 23
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      24     0  this   Lcom/lesson8/SynchronizedDemo;
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 20
          locals = [ class com/lesson8/SynchronizedDemo, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 2
}
SourceFile: "SynchronizedDemo.java"

分析如上代码发现synchronized修饰实例方法、synchronized静态方法synchronized修饰代码块不一样,synchronized修饰实例方法和synchronized静态方法一样

synchronized修饰代码块

涉及两条指令:

  • monitorenter:每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:

如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。

如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1。

如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

  • monitorexit:执行monitorexit的线程必须是objectref所对应的monitor的所有者。

指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个monitor 的所有权。

synchronized修饰实例方法

synchronized静态方法

从反编译的结果来看,方法的同步并没有通过指令monitorenter和monitorexit来完成(理论上其实也可以通过这两条指令来实现)。相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。

JVM就是根据该标示符来实现方法的同步的:当方法被调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 BUG弄潮儿 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
腾讯云代码分析
腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档