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

Monitor支持两种同步机制:

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

Monitor的模型如下:

exiting_exiting中文翻译_exiting

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能够共享的数据包括两部分:

monitor region 标识的方式

同步代码块

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)来存储锁对象方法上使用没有创建一个 异常表。

感谢您的观看,喜欢的小伙伴可以点个赞!!!专注Java、大数据知识干货及相关领域动态分享,请多多关注哦!

限 时 特 惠: 本站每日持续更新海量各大内部创业教程,一年会员只需98元,全站资源免费下载 点击查看详情
站 长 微 信: lzxmw777

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注