Java多线程复习与巩固(四)--synchronized的实现
来源:互联网 发布:日上免税店有mac吗 编辑:程序博客网 时间:2024/05/29 06:37
小小的回顾
在上一篇文章的例子中有一个Counter类:
static class Counter { private int c = 0; public void increment() { c++; } public void decrement() { c--; } public int value() { return c; }}
为了实现线程同步我们使用了synchronized
关键字,而synchronized
关键字有两种用法:
同步方法:
static class Counter {private int c = 0;public synchronized void increment() { c++; }public synchronized void decrement() { c--; }public int value() { return c; }}
同步代码块:
static class Counter {private int c = 0;public void increment() { synchronized(this){c++;} }public void decrement() { synchronized(this){c--;} }public int value() { return c; }}
反编译代码
我们把上面三段代码反编译一下,并取出increment
和decrement
两个方法的反编译代码:
未加同步
public void increment(); descriptor: ()V flags: ACC_PUBLIC Code: stack=3, locals=1, args_size=1 0: aload_0 1: dup 2: getfield #2 // Field c:I 5: iconst_1 6: iadd 7: putfield #2 // Field c:I 10: return LineNumberTable: line 3: 0 public void decrement(); descriptor: ()V flags: ACC_PUBLIC Code: stack=3, locals=1, args_size=1 0: aload_0 1: dup 2: getfield #2 // Field c:I 5: iconst_1 6: isub 7: putfield #2 // Field c:I 10: return LineNumberTable: line 4: 0
同步方法
public synchronized void increment(); descriptor: ()V flags: ACC_PUBLIC, ACC_SYNCHRONIZED Code: stack=3, locals=1, args_size=1 0: aload_0 1: dup 2: getfield #2 // Field c:I 5: iconst_1 6: iadd 7: putfield #2 // Field c:I 10: return LineNumberTable: line 3: 0 public synchronized void decrement(); descriptor: ()V flags: ACC_PUBLIC, ACC_SYNCHRONIZED Code: stack=3, locals=1, args_size=1 0: aload_0 1: dup 2: getfield #2 // Field c:I 5: iconst_1 6: isub 7: putfield #2 // Field c:I 10: return LineNumberTable: line 4: 0
同步代码块
public void increment(); descriptor: ()V flags: ACC_PUBLIC Code: stack=3, locals=3, args_size=1 0: aload_0 1: dup 2: astore_1 3: monitorenter 4: aload_0 5: dup 6: getfield #2 // Field c:I 9: iconst_1 10: iadd 11: putfield #2 // Field c:I 14: aload_1 15: monitorexit 16: goto 24 19: astore_2 20: aload_1 21: monitorexit 22: aload_2 23: athrow 24: return Exception table: from to target type 4 16 19 any 19 22 19 any LineNumberTable: line 3: 0 StackMapTable: number_of_entries = 2 frame_type = 255 /* full_frame */ offset_delta = 19 locals = [ class Counter, class java/lang/Object ] stack = [ class java/lang/Throwable ] frame_type = 250 /* chop */ offset_delta = 4 public void decrement(); descriptor: ()V flags: ACC_PUBLIC Code: stack=3, locals=3, args_size=1 0: aload_0 1: dup 2: astore_1 3: monitorenter 4: aload_0 5: dup 6: getfield #2 // Field c:I 9: iconst_1 10: isub 11: putfield #2 // Field c:I 14: aload_1 15: monitorexit 16: goto 24 19: astore_2 20: aload_1 21: monitorexit 22: aload_2 23: athrow 24: return Exception table: from to target type 4 16 19 any 19 22 19 any LineNumberTable: line 4: 0 StackMapTable: number_of_entries = 2 frame_type = 255 /* full_frame */ offset_delta = 19 locals = [ class Counter, class java/lang/Object ] stack = [ class java/lang/Throwable ] frame_type = 250 /* chop */ offset_delta = 4
Oracle官网和维基百科有相关指令的介绍
通过看底层字节码,可以看出以下几点:
- 普通方法和
synchronized
方法在方法内部没有任何区别,仅仅是synchronized
方法比普通方法多了一个ACC_SYNCHRONIZED
标志位,该标志位表示访问该方法需要同步访问(synchronized access) synchronized
同步代码块中由于要加载this引用,多了很多指令,而关键的两个指令是monitorenter
,monitorexit
。
Oracle官网提供的JVM规范中对monitorenter
和monitorexit
有以下介绍:
monitorenter指令用于进入对象的monitor
监视器,该指令的操作数是栈中的对象引用(objectref),这个对象引用必须是引用类型,不能使基本类型。每个对象都关联着一个monitor
监视器,当且仅当monitor
监视器有线程所有者(owner)才会被锁住,线程通过执行monitorenter
指令尝试获取对象关联的monitor
监视器的拥有权:
- 如果一个对象(objectref)关联的
monitor
监视器的entry
进入次数为0,该线程进入monitor
监视器并将它的entry
进入次数设置为1。该线程则是monitor
的所有者。 - 如果一个线程已经拥有对象(objectref)关联的
monitor
监视器,就让它再次进入该monitor
监视器,并将entry
进入次数加一。 - 如果另一个线程已经拥有了对象(objectref)关联的
monitor
监视器,那么该线程将会阻塞,直到monitor
监视器的entry
进入次数为0时再次尝试获取所有权。
注意:
- 如果引用对象为null,
monitorenter
指令将会抛出空指针异常。 monitorenter
指令可以配合monitorexit
指令来实现synchronized
代码块。虽然它们提供了锁定语义,但在synchronized
方法中并不会执行这两个指令,而是在调用synchronized
方法时monitor
进入,在方法返回时monitor
退出。- Java的同步机制除了要实现
monitorenter
和monitorexit
这样的操作,还应该包括等待monitor
监视器(Object.wait),通知等待monitor
监视器的其他线程(Object.notify和Object.notifyAll)。JVM指令中不会提供这些操作的支持。
monitorexit用于退出对象的monitor
监视器:
- 执行
monitorexit
指令的线程必须是该monitor
监视器的所有者。执行该指令时,该线程会将monitor
的entry
进入次数减一。如果entry
进入次数的结果为0,该线程将退出monitor
不再是它的所有者。其他进入monitor
的阻塞线程可以尝试获取该monitor
监视器。
JVM规范文档说的非常清楚明白,synchronized
关键字是由monitor
监视器实现的。
操作系统中经常把monitor称为“管程”
阅读全文
0 0
- Java多线程复习与巩固(四)--synchronized的实现
- Java多线程复习与巩固(一)--线程基本使用
- Java多线程复习与巩固(三)--线程同步
- Java多线程复习与巩固(七)--原子性操作
- Java多线程复习与巩固(六)--线程池ThreadPoolExecutor
- Java多线程复习与巩固(二)--线程相关工具类的使用
- Java多线程复习与巩固(八)--volatile关键字与CAS操作
- Java多线程中Synchronized的实现原理与优化
- Java语言基础复习与巩固
- Java多线程复习与巩固(五)-生产者消费者问题(第一部分)
- synchronized与static synchronized 的区别、synchronized在JVM底层的实现原理及Java多线程锁理解
- synchronized与static synchronized 的区别、synchronized在JVM底层的实现原理及Java多线程锁理解 (r)
- HTML复习与巩固
- java多线程系列四:synchronized
- 复习巩固java基础知识
- Java多线程编程(四)—浅谈synchronized与lock
- Java多线程编程(四)—浅谈synchronized与lock
- Java多线程编程(四)—浅谈synchronized与lock
- 【每天一点linux】多线程编程之生产者消费者模型
- Spring模块
- JZ2440驱动编译,安装和测试
- virtualbox 下面设置共享文件夹 su:鉴定故障
- 通过Maven运行 MyBatis Generator
- Java多线程复习与巩固(四)--synchronized的实现
- EL与JSTL
- 【OpenCV3图像处理】提取轮廓的凸包、外包矩形、最小外包矩形、最小外包圆
- Assign Cookies
- 我的网络编程学习之路——主机字节序和网络字节序
- BZOJ 3620: 似乎在梦中见过的样子(kmp)
- NZT 紧急通知
- 使gridview的actioncolumn的图标不再换行
- 大小写互换