资讯专栏INFORMATION COLUMN

java synchronize - 线程同步机制

EscapedDog / 1957人阅读

摘要:就像是拥有一个特殊房间的建筑,在同一时间里,这间特殊的房间只能被一个线程拥有。当线程获得这就是一个信号,线程开始拥有的所有权,能够继续执行。注意到的以及要唤醒的对象就处于换句话说,如果退出的线程没有执行那么只有能够获取执行的权限。

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

enter the monitor:进入这幢建筑

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

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

releasing the monitor:离开特殊的房间

exiting the monitor:离开这幢建筑

Monitor支持两种同步机制:

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

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

</>复制代码

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

Monitor的模型如下:

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

</>复制代码

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

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

对象锁

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

实例对象:存储在堆中

类实例:存储在方法区,当锁一个类的时候,实际上就是锁类的 Class 对象

对于局部变量,他们存储在栈中,属于线程私有,不会存在共享一说。
单个线程可以同时锁住一个对象多次,JVM会记住锁住的总次数,每一次释放锁,总次数减一,只有在这个次数变成0的时候,这个锁才有可能被其它线程持有

monitor region 标识的方式

同步代码块

同步方法

JVM使用的指令为

monitorenter 获取引用对象的锁

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

同步代码块

</>复制代码

  1. public class SynchronizedTest {
  2. private int i=0;
  3. public void syn(){
  4. synchronized (this){
  5. i++;
  6. }
  7. }
  8. }

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

</>复制代码

  1. public class main.lockTest.SynchronizedTest {
  2. public main.lockTest.SynchronizedTest();
  3. Code:
  4. 0: aload_0
  5. 1: invokespecial #1 // Method java/lang/Object."":()V
  6. 4: aload_0
  7. 5: iconst_0
  8. 6: putfield #2 // Field i:I
  9. 9: return
  10. public void syn();
  11. Code:
  12. 0: aload_0 // aload_0 属于 aload_ 系列指令的一种。表示获取一个本地变量的引用,然后放入栈中
  13. 1: dup //弹出栈顶的单字节,然后入栈两次,相当于拷贝了栈顶元素
  14. 2: astore_1 // astore_系列指令的一种。从栈顶获取对象的引用,并存入本地变量
  15. 3: monitorenter //获取引用对象的锁
  16. 4: aload_0
  17. 5: dup
  18. 6: getfield #2 // Field i:I 从栈中获取对象的引用,然后得到它的值
  19. 9: iconst_1 // iconst_ 的一种,将常量放入栈中
  20. 10: iadd // 从操作栈中弹出两个integer,把他们相加,然后将结果重新存入栈中
  21. 11: putfield #2 // Field i:I 将值存入引用对象
  22. 14: aload_1
  23. 15: monitorexit // 释放引用对象的锁
  24. 16: goto 24 // 跳转到下一个指令的位置
  25. 19: astore_2
  26. 20: aload_1
  27. 21: monitorexit
  28. 22: aload_2
  29. 23: athrow //从操作栈中删掉对象的引用,并抛出这个对象的异常
  30. 24: return //执行返回
  31. Exception table:
  32. from to target type
  33. 4 16 19 any
  34. 19 22 19 any
  35. }

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

同步方法

</>复制代码

  1. public class SynchronizedTest {
  2. private int i=0;
  3. public synchronized void syn(int i){
  4. i++;
  5. }
  6. }

对应指令如下

</>复制代码

  1. public class main.lockTest.SynchronizedTest {
  2. public main.lockTest.SynchronizedTest();
  3. Code:
  4. 0: aload_0
  5. 1: invokespecial #1 // Method java/lang/Object."":()V
  6. 4: aload_0
  7. 5: iconst_0
  8. 6: putfield #2 // Field i:I
  9. 9: return
  10. public synchronized void syn(int);
  11. Code:
  12. 0: iinc 1, 1
  13. 3: return
  14. }

可以看到两个区别

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

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

synchronize原理
java 虚拟机指令

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/72389.html

相关文章

  • JAVA线程机制解析-volatile&synchronized

    摘要:当一个线程持有重量级锁时,另外一个线程就会被直接踢到同步队列中等待。 java代码先编译成字节码,字节码最后编译成cpu指令,因此Java的多线程实现最终依赖于jvm和cpu的实现 synchronized和volatile 我们先来讨论一下volatile关键字的作用以及实现机制,每个线程看到的用volatile修饰的变量的值都是最新的,更深入的解释就涉及到Java的内存模型了,我们...

    dendoink 评论0 收藏0
  • Java 中的 Monitor 机制

    摘要:基本元素机制需要几个元素来配合,分别是临界区对象及锁条件变量以及定义在对象上的,操作。这个外部条件在机制中称为条件变量。提供的机制,其实是,等元素合作形成的,甚至说外部的条件变量也是个组成部分。 monitor的概念 管程,英文是 Monitor,也常被翻译为监视器,monitor 不管是翻译为管程还是监视器,都是比较晦涩的,通过翻译后的中文,并无法对 monitor 达到一个直观的描...

    Jacendfeng 评论0 收藏0
  • Java中的synchronized关键字

    摘要:的关键字中的块使用关键字进行标记。由于每个类只有一个类对象存在于中,因此全局同时只有一个线程能够进入到同一个类的静态同步方法中。同步代码块使这种期望成为可能。注意同步代码块如何在括号中接受一个对象。相同的实例被传入两个不同的线程实例中。 Java的synchronized块标记一个方法或一个代码块为同步的。synchronized块能用于防止出现竞态条件。 Java的synchroni...

    lylwyy2016 评论0 收藏0
  • Java线程学习(四)等待/通知(wait/notify)机制

    摘要:运行可运行状态的线程获得了时间片,执行程序代码。阻塞的情况分三种一等待阻塞运行的线程执行方法,会把该线程放入等待队列中。死亡线程方法执行结束,或者因异常退出了方法,则该线程结束生命周期。死亡的线程不可再次复生。 系列文章传送门: Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1) java多线程学习(二)synchronized关键...

    PiscesYE 评论0 收藏0
  • 值得保存的 synchronized 关键字总结

    摘要:无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。另外在中引入了自适应的自旋锁。和关键字的总结推荐一 该文已加入开源文档:JavaGuide(一份涵盖大部分Java程序员所需要掌握的核心知识)。地址:https://github.com/Snailclimb... 本文是对 synchronized 关键字使用、底层原理、JD...

    miguel.jiang 评论0 收藏0

发表评论

0条评论

EscapedDog

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<