java astore_java synchronize - 线程同步机制

Java支持同步机制的是Monitor。Monitor就像是拥有一个特殊房间的建筑,在同一时间里,这间特殊的房间只能被一个线程拥有。

enter the monitor:进入这幢建筑

acquiring the monitor:进入建筑里的特殊房间

owning the monitor:拥有特殊房间的所有权

releasing the monitor:离开特殊的房间

exiting the monitor:离开这幢建筑

Monitor支持两种同步机制:

互斥:通过对象锁,使得多线程处理能互相独立的处理共享数据,而不会发生线程不安全

协作:通过对象的wait和notify方法实现,比如一个读的线程从缓冲区读数据,另一个线程负责往缓冲区写数据,如果缓冲区没有数据,则读线程阻塞,有数据时,读线程就要开始消费

wait-notify又可以称作’Singal-continue’。当线程获得 notify,这就是一个信号,线程开始拥有 monitor的所有权,能够 继续 执行 monitor region。执行完之后,此线程释放monitor,一个等待的线程则会获得一样的机会

Monitor的模型如下:

a9492d950d752950ccb8b09a40afb974.png

1 表示线程刚到达 monitor region ,即 enter the monitor

2 表示线程获取 monitor的所有权,即acquiring the monitor

3 表示线程执行了 wait,交出所有权,即releasing the monitor

4 表示原来拥有 monitor 的线程执行了 notify ,恰好被这个线程获取所有权

5 表示线程执行完了 monitor region,即exiting the monitor

Monitor特意把等待的线程分成了两个部分,Entry Set和Wait Set,Entry Set表示线程刚执行到 Monitor region,而Wait Set则是由于线程执行了wait方法而进入的区域。注意到Object的 notify 以及 notifyAll 要唤醒的对象就处于 Wait Set,换句话说,如果退出 monitor 的线程没有执行 notify/notifyAll ,那么只有 Entry Set 能够获取执行的权限 。如果执行了,则Entry Set和Wait Set中所有的线程都会竞争谁最终能够获取 monitor 的能力

一个线程要离开Wait Set,要么是原拥有 monitor 的线程执行了 notify/notifyAll,要么是wait的时间到了,JVM 会触发一个notify

对象锁

Java能够共享的数据包括两部分:

实例对象:存储在堆中

类实例:存储在方法区,当锁一个类的时候,实际上就是锁类的 Class 对象 对于局部变量,他们存储在栈中,属于线程私有,不会存在共享一说。

单个线程可以同时锁住一个对象多次,JVM会记住锁住的总次数,每一次释放锁,总次数减一,只有在这个次数变成0的时候,这个锁才有可能被其它线程持有

monitor region 标识的方式

同步代码块

同步方法 JVM使用的指令为

monitorenter 获取引用对象的锁

monitorexit 是否在monitorenter处获得的对象锁

同步代码块

public class SynchronizedTest {

private int i=0;

public void syn(){

synchronized (this){

i++;

}

}

}

javap -c SynchronizedTest.class 执行后对应的指令如下

public class main.lockTest.SynchronizedTest {

public main.lockTest.SynchronizedTest();

Code:

0: aload_0

1: invokespecial #1 // Method java/lang/Object."":()V

4: aload_0

5: iconst_0

6: putfield #2 // Field i:I

9: return

public void syn();

Code:

0: aload_0 // aload_0 属于 aload_ 系列指令的一种。表示获取一个本地变量的引用,然后放入栈中

1: dup //弹出栈顶的单字节,然后入栈两次,相当于拷贝了栈顶元素

2: astore_1 // astore_系列指令的一种。从栈顶获取对象的引用,并存入本地变量

3: monitorenter //获取引用对象的锁

4: aload_0

5: dup

6: getfield #2 // Field i:I 从栈中获取对象的引用,然后得到它的值

9: iconst_1 // iconst_ 的一种,将常量放入栈中

10: iadd // 从操作栈中弹出两个integer,把他们相加,然后将结果重新存入栈中

11: putfield #2 // Field i: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

}

注意到,如果抛出了异常,也会执行 monitorexit 。印证了无论如何,只要离开了monitor region,锁都会被释放

同步方法

public class SynchronizedTest {

private int i=0;

public synchronized void syn(int i){

i++;

}

}

对应指令如下

public class main.lockTest.SynchronizedTest {

public main.lockTest.SynchronizedTest();

Code:

0: aload_0

1: invokespecial #1 // Method java/lang/Object."":()V

4: aload_0

5: iconst_0

6: putfield #2 // Field i:I

9: return

public synchronized void syn(int);

Code:

0: iinc 1, 1

3: return

}

可以看到两个区别

在方法上使用 synchronized 没有用到 monitorenter / monitorexit 指令。这是因为在方法上使用synchronized并不需要一个本地变量槽(slot)来存储锁对象

方法上使用没有创建一个 异常表

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值