java并发之synchronized深入学习记录
synchronized关键字是java中很常用的一种关键字,但是大多数人还是停留在会用的基础上,对于synchronized关键字的底层的原理实现还是一知半解,作为java中一个很常用也很重要的关键字,synchronized的底层实现对于加速并发深入了解起到了很大的作用。
1.synchronized常用的场景
普通同步方法(实例方法),锁是当前实例对象 ,进入同步代码前要获得当前实例的锁
//java普通同步方法,默认取得就是当前对象
public synchronized void method1() {
System.out.println("普通同步方法")
}
静态同步方法,锁是当前类的class对象 ,进入同步代码前要获得当前类对象的锁
//java普通同步方法,默认取得就是当前对象
public static synchronized void method2() {
System.out.println("当前类class对象的同步方法")
}
同步方法块,锁是括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁
public void method3() {
//this对象可以换成任何一个对象,默认this就是当前对象本身
synchronized(this) {
System.out.println("同步方法块")
}
}
2.synchronized关键字不同用法下的反编译
上述三种为大家常用的方法,下面我一步步将上面的三种方法进行反编译,看看反编译之后的代码是怎样的
下面展示一下同步代码块方法:
public void method1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=1
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #21 // String Hello World!
9: invokevirtual #23 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: aload_1
13: monitorexit
14: goto 20
17: aload_1
18: monitorexit
19: athrow
反编译之后,大家可以从上述的指令集中看到monitorenter和monitorexit这两个指令,下面我来解释一下这两个指令集的意思
- monitorenter:monitorenter指向一个对象,这个对象里面放着锁的各种信息和重入次数,当一个线程执行到monitorenter这个指令的时候,这个线程会尝试获取该对象的monitor所有权,获取成功了,就表示持有了这把锁,同时monitor的进入次数加1,如果是多次重入,则会多次加1。
- monitorexit:monitorexit在方法的结束的地方,每次执行重入次数都会减1,直到结束重入次数为0,也就表示持有锁的线程释放了这把锁,上述字节码文件存在两个monitorexit,这是jvm在程序发生异常的处理过程中,由jvm加的一个monitorexit。
下面展示一下普通同步方法:
public synchronized void method1();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=1, args_size=1
0: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #21 // String Hello World!
5: invokevirtual #23 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 6: 0
line 7: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this Lcom/test/netty/SynchronizedTest;
}
反编译之后似乎并没有看见monitorenter和monitorexit,到这里有些人就比较疑惑了,虽然这段字节码指令中并没有看见这两个字段,但是却有一个新的指令ACC_SYNCHRONIZED,看到这个指令是不是比较眼熟,确实,这个指令和我们java中的同步关键字synchronized很像,JVM就是通过这个标识符来实现方法同步的。
- 当同步方法被调用时,jvm将会检查该方法的 ACC_SYNCHRONIZED是否被设置,如果设置了,线程将会直接访问当前对象的monitor,所以同步方法本质上其实还是调用monitor.
静态同步方法
public static synchronized void method3();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=0, args_size=0
0: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream;
3: sipush 1111
6: invokevirtual #21 // Method java/io/PrintStream.println:(I)V
9: return
LineNumberTable:
line 8: 0
line 10: 9
LocalVariableTable:
Start Length Slot Name Signature
上述字节码文件大家发现没有,其实和普通同步方法并没有什么区别,只是多了一个静态字节码指令,所以调用的本质还是一样的,这里就不多做解释了。
今天的学习记录就写到这里,如果有兴趣的话,可以继续看下一篇文章