synchronized,是Java中用于解决并发情况下数据同步访问的一个很重要的关键字。当我们想要保证一个共享资源在同一时间只会被一个线程访问到时,我们可以在代码中使用synchronized关键字对类或者对象加锁。那么,本文来介绍一下synchronized关键字的实现原理是什么。在阅读本文之间,建议先看下Java虚拟机是如何执行线程同步的 。

反编译

众所周知,在Java中,synchronized有两种使用形式,同步方法和同步代码块。代码如下:

/*** @author Hollis 17/11/9.*/
public class SynchronizedTest {public synchronized void doSth(){System.out.println("Hello World");}public void doSth1(){synchronized (SynchronizedTest.class){System.out.println("Hello World");}}
}

我们先来使用Javap来反编译以上代码,结果如下(部分无用信息过滤掉了):

  public synchronized void doSth();descriptor: ()Vflags: ACC_PUBLIC, ACC_SYNCHRONIZEDCode:stack=2, locals=1, args_size=10: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;3: ldc           #3                  // String Hello World5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V8: returnpublic void doSth1();descriptor: ()Vflags: ACC_PUBLICCode:stack=2, locals=3, args_size=10: ldc           #5                  // class com/hollis/SynchronizedTest2: dup3: astore_14: monitorenter5: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;8: ldc           #3                  // String Hello World10: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V13: aload_114: monitorexit15: goto          2318: astore_219: aload_120: monitorexit21: aload_222: athrow23: return

反编译后,我们可以看到Java编译器为我们生成的字节码。在对于doSthdoSth1的处理上稍有不同。也就是说。JVM对于同步方法和同步代码块的处理方式不同。

对于同步方法,JVM采用ACC_SYNCHRONIZED标记符来实现同步。 对于同步代码块。JVM采用monitorentermonitorexit两个指令来实现同步。

关于这部分内容,在JVM规范中也可以找到相关的描述。

同步方法

The Java® Virtual Machine Specification中有关于方法级同步的介绍:

Method-level synchronization is performed implicitly, as part of method invocation and return. A synchronized method is distinguished in the run-time constant pool’s method_info structure by the ACC_SYNCHRONIZED flag, which is checked by the method invocation instructions. When invoking a method for which ACC_SYNCHRONIZED is set, the executing thread enters a monitor, invokes the method itself, and exits the monitor whether the method invocation completes normally or abruptly. During the time the executing thread owns the monitor, no other thread may enter it. If an exception is thrown during invocation of the synchronized method and the synchronized method does not handle the exception, the monitor for the method is automatically exited before the exception is rethrown out of the synchronized method.

主要说的是: 方法级的同步是隐式的。同步方法的常量池中会有一个ACC_SYNCHRONIZED标志。当某个线程要访问某个方法的时候,会检查是否有ACC_SYNCHRONIZED,如果有设置,则需要先获得监视器锁,然后开始执行方法,方法执行之后再释放监视器锁。这时如果其他线程来请求执行方法,会因为无法获得监视器锁而被阻断住。值得注意的是,如果在方法执行过程中,发生了异常,并且方法内部并没有处理该异常,那么在异常被抛到方法外面之前监视器锁会被自动释放。

同步代码块

同步代码块使用monitorentermonitorexit两个指令实现。 The Java® Virtual Machine Specification 中有关于这两个指令的介绍:

monitorenter

Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:

If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.

If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.

If another thread already owns the monitor associated with objectref, the thread blocks until the monitor’s entry count is zero, then tries again to gain ownership.

monitorexit

The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.

The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.

大致内容如下: 可以把执行monitorenter指令理解为加锁,执行monitorexit理解为释放锁。 每个对象维护着一个记录着被锁次数的计数器。未被锁定的对象的该计数器为0,当一个线程获得锁(执行monitorenter)后,该计数器自增变为 1 ,当同一个线程再次获得该对象的锁的时候,计数器再次自增。当同一个线程释放锁(执行monitorexit指令)的时候,计数器再自减。当计数器为0的时候。锁将被释放,其他线程便可以获得锁。

总结

同步方法通过ACC_SYNCHRONIZED关键字隐式的对方法进行加锁。当线程要执行的方法被标注上ACC_SYNCHRONIZED时,需要先获得锁才能执行该方法。

同步代码块通过monitorentermonitorexit执行来进行加锁。当线程执行到monitorenter的时候要先获得所锁,才能执行后面的方法。当线程执行到monitorexit的时候则要释放锁。

每个对象自身维护这一个被加锁次数的计数器,当计数器数字为0时表示可以被任意线程获得锁。当计数器不为0时,只有获得锁的线程才能再次获得锁。即可重入锁。

至此,我们大致了解了Synchronized的原理。但是还有几个问题并没有介绍清楚,比如,Monitor到底是什么?对象的锁的状态保存在哪里? 别急,后面会再介绍。

from: https://www.hollischuang.com/archives/1883

深入理解多线程(一)——Synchronized的实现原理相关推荐

  1. JUC多线程:synchronized锁机制原理 与 Lock锁机制

    前言: 线程安全是并发编程中的重要关注点,造成线程安全问题的主要原因有两点,一是存在共享数据(也称临界资源),二是存在多条线程共同操作共享数据.因此为了解决这个问题,我们可能需要这样一个方案,当存在多 ...

  2. 【多线程】Synchronized及实现原理

    Synchronized的基本使用 Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法. Synchronized的作用主要有三个: 确保线程互斥的访问同步代码 ...

  3. 深入理解多线程(五)—— Java虚拟机的锁优化技术

    本文是<深入理解多线程>的第五篇文章,前面几篇文章中我们从synchronized的实现原理开始,一直介绍到了Monitor的实现原理. 前情提要 通过前面几篇文章,我们已经知道: 1.同 ...

  4. 【死磕Java并发】—–深入分析synchronized的实现原理

    记得刚刚开始学习Java的时候,一遇到多线程情况就是synchronized,相对于当时的我们来说synchronized是这么的神奇而又强大,那个时候我们赋予它一个名字"同步", ...

  5. synchronized的实现原理

    说明:本文基于Hollischuang大神的文章! ** 一:synchronized的原理 ** 在再有人问你Java内存模型是什么,就把这篇文章发给他.中我们曾经介绍过,Java语言为了解决并发编 ...

  6. synchronized原理_浅谈synchronized的实现原理

    Synchronized是Java中的重量级锁,在我刚学Java多线程编程时,我只知道它的实现和monitor有关,但是synchronized和monitor的关系,以及monitor的本质究竟是什 ...

  7. Java多线程:synchronized | Volatile 和Lock和ReadWriteLock多方位剖析(一)

    前言 本文站在多线程初中级学习者的角度,较为全面系统的带你一起了解多线程与锁相关的知识点.带你一起解开与锁相关的各种概念.用法.利弊等.比如:synchronized.Volatile.Lock.Re ...

  8. 死磕Java并发:深入分析synchronized的实现原理

    本文转载自公众号: Java技术驿站 记得刚刚开始学习Java的时候,一遇到多线程情况就是synchronized.对于当时的我们来说,synchronized是如此的神奇且强大.我们赋予它一个名字& ...

  9. Java Synchronized 重量级锁原理深入剖析上(互斥篇)

    前言 线程并发系列文章: Java 线程基础 Java 线程状态 Java "优雅"地中断线程-实践篇 Java "优雅"地中断线程-原理篇 真正理解Java ...

  10. Android开发中synchronized的实现原理

    synchronized的三种使用方式 **1.修饰实例方法,**作用于当前实例加锁,进入同步代码前要获得当前实例的锁. 没有问题的写法: public class AccountingSync im ...

最新文章

  1. LeetCode Increasing Triplet Subsequence(动态规划)
  2. java 图形校验_java图形验证码生成工具类 web页面校验验证码
  3. 老板,年终奖我不要了,请给我一部华为Mate RS保时捷设计手机
  4. 第三次学JAVA再学不好就吃翔(part45)--Object类
  5. 快速开发后台不用太多代码的 tp5_小程序·云开发优劣简述
  6. springboot 获取登录浏览器_java项目部署到linux服务器,微信小程序后台springboot项目部署到云服务器(图文详解)...
  7. 光耦在短距离通信中的应用
  8. Simulink之绝缘栅双极型晶体管(IGBT)
  9. Java设计模式--解释器模式
  10. 删除顺序表中重复元素,并按照原序输出
  11. 深度学习花书-5.4 估计、偏差和方差
  12. Android如何在免Root下自动Pay(非人为非无障碍),引起的深思?
  13. thinkpadt410接口介绍_Thinkpad-T410 T410S笔记本左右接口以及硬件设备详细介绍!
  14. 《认知天性》听后感及听书笔记
  15. 工程上为什么常用3dB带宽?而不是1dB或者2dB
  16. 未知usb设备(设备描述请求失败)_HomePod mini?电源线同样不可拆卸:但或能用USB-C移动电源供电...
  17. LodeRunner2的Wacky Wall 11关
  18. 词霸天下---141词根 【-habit- = -hibit- 拥有,占据,居住 】
  19. 高德地图 鼠标点击后 地图上点标记跟着变化
  20. 百度收购91无线,效果未必如愿

热门文章

  1. ReflectionToStringBuilder
  2. redis 模糊删除实现
  3. CML 2020 | 显式引入对分类标签的描述,如何提高文本分类的效果?
  4. 把自己当成打工的,一辈子都是打工的!:周鸿祎
  5. 看了蚂蚁金服的布局,你或能明白马云为何一定要将支付宝独立了!
  6. 思路+步骤+方法,三步教你如何快速构建用户画像?
  7. Java 8 - 收集器Collectors_归约和汇总
  8. RocketMQ-初体验RocketMQ(11)-过滤消息_自定义Java类筛选消息
  9. Quartz-Java Web项目中使用Quartz
  10. C++五子棋(五)——实现AI落子