Synchronized修饰方法和同步代码块的区别

本文探讨了Synchronized修饰方法与同步代码块在Java中的区别和底层实现原理。通过代码验证和反编译分析,展示了synchronized修饰实例方法、静态方法时通过ACC_SYNCHRONIZED标志实现同步,而同步代码块则使用monitorenter和monitorexit指令。同步方法在执行前后自动获取和释放监视器锁,同步代码块则根据计数器管理锁的获取与释放。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题出现

Synchronized修饰方法和同步代码块的区别

Synchronized修饰方法的底层实现原理

Synchronized修饰同步代码块底层实现原理

代码验证

synchronized用法我们知道可以修饰实例方法,也可以修饰静态方法,还可以使用同步代码块。

public class Service {

    //synchronized修饰实例方法
    synchronized public  void printA(){
        System.out.println("I am printA");
            }

    //synchronized修饰静态方法
    synchronized public static void printB(){
               System.out.println("I am printB");
            }

    //synchronized同步代码块
     public void printC(){
         synchronized (this) {
             System.out.println("I am printC");

        }
     }
}

反编译看

1.使用javac先编译

D:\vm>javac -encoding UTF-8 Service.java

2.使用javap查看编译后的代码

D:\vm>javap -verbose Service

修饰实例方法

public synchronized void printA();
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljav
a/io/PrintStream;
         3: ldc           #3                  // String I am printA
         5: invokevirtual #4                  // Method java/io/PrintStream.prin
tln:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 7: 0
        line 8: 8

修饰静态方法

  public static synchronized void printB();
    flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=0, args_size=0
         0: getstatic     #2                  // Field java/lang/System.out:Ljav
a/io/PrintStream;
         3: ldc           #5                  // String I am printB
         5: invokevirtual #4                  // Method java/io/PrintStream.prin
tln:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 12: 0
        line 13: 8

同步代码块

 public void printC();
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         3: monitorenter
         4: getstatic     #2                  // Field java/lang/System.out:Ljav
a/io/PrintStream;
         7: ldc           #6                  // String I am printC
         9: invokevirtual #4                  // Method java/io/PrintStream.prin
tln:(Ljava/lang/String;)V
        12: aload_1
        13: monitorexit
        14: goto          22
        17: astore_2
        18: aload_1
        19: monitorexit
        20: aload_2
        21: athrow
        22: return

从上面可以看出来,synchronized修饰实例方法或静态方法都是通过标识ACC_SYNCHRONIZED实现同步。而同步代码块是是采用monitorenter、monitorexit两个指令来实现同步。

synchronized修饰方法

synchronized修饰方法,同步是隐式的。当某个线程要访问某个方法的时候,会检查是否有ACC_SYNCHRONIZED,如有,则需要先获取监视器锁,然后才开始执行方法。方法执行之后再释放监视器锁。在线程执行方法的时候,有另外线程也来请求执行该方法,会因为无法获取监视器锁而被阻断。

同步代码块

同步代码块是是采用monitorenter、monitorexit两个指令来实现同步。在执行monitorenter指令时,首先要尝试获取对象的锁。如果这个对象没被锁定,或者当前线程已经拥有了那个对象的锁,把锁的计数器加1,相应的,在执行monitorexit指令时会将锁计数器减1,当计数器为0的时候,锁就会被释放。如果获取对象锁失败,那当前线程就要阻塞等待。直到对象锁被另外一个线程释放为止。

monitorenter、monitorexit这两个字节码指令都需要一个reference类型的参数来明确要锁定和解锁的对象。上例中使用了synchronized (this)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值