Synchronized 使用表现形式

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情

synchronized 使用方式

Java 中的每个对象都可以作为锁,具体表现为以下三种形式:

  1. 修饰普通方法,锁是当前实例对象;
  2. 修饰静态同步方法,锁是当前类的 Class 对象
  3. 修饰代码块,锁是 synchronized 括号里配置的对象;

下面通过举例看一下这几种加锁方式

同步方法

public synchronized void addI 同步方法上锁,使用命令将 javap -c -v FileName.class 文件转换成字节码指令后,

核心结果如下:

public void addII(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: ACC_PUBLIC
Code:
  stack=3, locals=3, args_size=2

.... 省略

public synchronized void addI(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: ACC_PUBLIC, java 
Code:
  stack=3, locals=3, args_size=2

复制代码

可以看到同步方法块上有 flag标记ACC_SYNCHRONIZED,表明此方法是同步的。非同步方法flags并没有ACC_SYNCHRONIZED标记。

静态方法同步

public class InternalVariable {

    public static void addIII(String username){
        // ... 代码省略
    }
}
复制代码

反编译查看核心编译结果:

public static synchronized void addIII(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
Code:
  stack=3, locals=3, args_size=1
复制代码

与普通方法相比,静态方法在 flags 标志位上多了一个 ACC_STATIC。

同步代码块

public class InternalVariable {
    public void addIIII(String username) {
        synchronized (this) {
            // ... 代码
        }
    }
}
复制代码

反编译结果如下:

  public void addIIII(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=6, args_size=2
         0: aload_0
         1: dup
         2: astore_2
         3: monitorenter
         // ... 省略
       135: invokevirtual #13                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       138: aload_2
       139: monitorexit
       140: goto          150
       143: astore        5
       145: aload_2
       146: monitorexit
       147: aload         5
       149: athrow
       150: return
复制代码

同步代码块有1个monitorenter,但是却有2个monitorexit,这是因为JVM为了保证方法在出异常时也能正常释放锁,编译器会自动产生一个异常处理器, 目的就是用来执行monitorexit指令

synchronized 底层实现

synchronized 底层实现依赖于 JVM,在 JVM 中同步的实现是通过监视器锁 monitorenter 和 monitorexit 指令实现,或者隐式地通过方法调用 (检测到同步标志位)和返回指令实现。这两种,前者对应同步代码块,后者对应同步方法、静态同步方法。

所以同步方法最终还是通过监视器锁来完成线程同步,并没有违背 JVM 中同步是通过 monitor 的进入和退出实现的。

猜你喜欢

转载自juejin.im/post/7085409584164110367