转自:http://www.cnblogs.com/paddix/p/5367116.html

如果对上面的执行结果还有疑问,也先不用急,我们先来了解Synchronized的原理,再回头上面的问题就一目了然了。我们先通过反编译下面的代码来看看Synchronized是如何实现对代码块进行同步的:

1 package com.paddx.test.concurrent;
2
3 public class SynchronizedDemo {
4 public void method() {
5 synchronized (this) {
6 System.out.println("Method 1 start");
7 }
8 }
9 }

反编译结果:

关于这两条指令的作用,我们直接参考JVM规范中描述:

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.

这段话的大概意思为:

每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:

1、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。

2、如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.

3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

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.

这段话的大概意思为:

执行monitorexit的线程必须是objectref所对应的monitor的所有者。

指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。

  通过这两段描述,我们应该能很清楚的看出Synchronized的实现原理,Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。

  我们再来看一下同步方法的反编译结果:

源代码:

1 package com.paddx.test.concurrent;
2
3 public class SynchronizedMethod {
4 public synchronized void method() {
5 System.out.println("Hello World!");
6 }
7 }

反编译结果:

  从反编译的结果来看,方法的同步并没有通过指令monitorenter和monitorexit来完成(理论上其实也可以通过这两条指令来实现),不过相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。JVM就是根据该标示符来实现方法的同步的:当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。

三、运行结果解释

  有了对Synchronized原理的认识,再来看上面的程序就可以迎刃而解了。

1、代码段2结果:

  虽然method1和method2是不同的方法,但是这两个方法都进行了同步,并且是通过同一个对象去调用的,所以调用之前都需要先去竞争同一个对象上的锁(monitor),也就只能互斥的获取到锁,因此,method1和method2只能顺序的执行。

2、代码段3结果:

  虽然test和test2属于不同对象,但是test和test2属于同一个类的不同实例,由于method1和method2都属于静态同步方法,所以调用的时候需要获取同一个类上monitor(每个类只对应一个class对象),所以也只能顺序的执行。

3、代码段4结果:

  对于代码块的同步实质上需要获取Synchronized关键字后面括号中对象的monitor,由于这段代码中括号的内容都是this,而method1和method2又是通过同一的对象去调用的,所以进入同步块之前需要去竞争同一个对象上的锁,因此只能顺序执行同步块。

四 总结

  Synchronized是Java并发编程中最常用的用于保证线程安全的方式,其使用相对也比较简单。但是如果能够深入了解其原理,对监视器锁等底层知识有所了解,一方面可以帮助我们正确的使用Synchronized关键字,另一方面也能够帮助我们更好的理解并发编程机制,有助我们在不同的情况下选择更优的并发策略来完成任务。对平时遇到的各种并发问题,也能够从容的应对。

synchronized的底层实现原理的更多相关文章

  1. 说一下 synchronized 底层实现原理?(未完成)

    说一下 synchronized 底层实现原理?(未完成)

  2. synchronized的底层原理?

    最近更新的XX必备系列适合直接背答案,不深究,不喜勿喷. 你能说简单说一下synchronize吗? 可别真简单一句话就说完了呀~ 参考回答: synchronize是java中的关键字,可以用来修饰 ...

  3. Java synchronized 关键字的实现原理

    数据同步需要依赖锁,那锁的同步又依赖谁?synchronized给出的答案是在软件层面依赖JVM,而Lock给出的方案是在硬件层面依赖特殊的CPU指令,大家可能会进一步追问:JVM底层又是如何实现sy ...

  4. Java并发之底层实现原理学习笔记

    本篇博文将介绍java并发底层的实现原理,我们知道java实现的并发操作最后肯定是由我们的CPU完成的,中间经历了将java源码编译成.class文件,然后进行加载,然后虚拟机执行引擎进行执行,解释为 ...

  5. 《Java并发编程的艺术》Java并发机制的底层实现原理(二)

    Java并发机制的底层实现原理 1.volatile volatile相当于轻量级的synchronized,在并发编程中保证数据的可见性,使用 valotile 修饰的变量,其内存模型会增加一个 L ...

  6. java并发编程系列七:volatile和sinchronized底层实现原理

    一.线程安全 1.  怎样让多线程下的类安全起来 无状态.加锁.让类不可变.栈封闭.安全的发布对象 2. 死锁 2.1 死锁概念及解决死锁的原则 一定发生在多个线程争夺多个资源里的情况下,发生的原因是 ...

  7. 那些年读过的书《Java并发编程的艺术》一、并发编程的挑战和并发机制的底层实现原理

    一.并发编程的挑战 1.上下文切换 (1)上下文切换的问题 在处理器上提供了强大的并行性就使得程序的并发成为了可能.处理器通过给不同的线程分配不同的时间片以实现线程执行的自动调度和切换,实现了程序并行 ...

  8. 多线程系列八:线程安全、Java内存模型(JMM)、底层实现原理

    一.线程安全 1.  怎样让多线程下的类安全起来 无状态.加锁.让类不可变.栈封闭.安全的发布对象 2. 死锁 2.1 死锁概念及解决死锁的原则 一定发生在多个线程争夺多个资源里的情况下,发生的原因是 ...

  9. (转载)JVM实现synchronized的底层机制

    目前在Java中存在两种锁机制:synchronized和Lock,Lock接口及其实现类是JDK5增加的内容,其作者是大名鼎鼎的并发专家Doug Lea.本文并不比较synchronized与Loc ...

随机推荐

  1. css3 transform中的matrix矩阵

    CSS3中的矩阵CSS3中的矩阵指的是一个方法,书写为matrix()和matrix3d(),前者是元素2D平面的移动变换(transform),后者则是3D变换.2D变换矩阵为3*3, 如上面矩阵示 ...

  2. 「版本升级」界面控件Kendo UI正式发布R2 2019|附下载

    通过70多个可自定义的UI组件,Kendo UI可以创建数据丰富的桌面.平板和移动Web应用程序.通过响应式的布局.强大的数据绑定.跨浏览器兼容性和即时使用的主题,Kendo UI将开发时间加快了50 ...

  3. spring security基本知识(四) WebSecurity

    1.创建一个Filter   现在web.xml文档中声明一个filter class="org".springframework.web.filter.DelegatingFil ...

  4. iOS 推送角标解决方案

    在App启动时:didFinishLaunchingWithOptions 方法中:application.applicationIconBadgeNumber = ; //角标清零 在读消息时: a ...

  5. IDEA将新建项目上传至GitLab

    1.首先,需要你自己登录GitLab,并新建一个项目的链接,如下图所示: (此图为图三,该链接下面操作中将会用到!) 2.在idea上新建一个项目,完成之后,需要创建一个git仓库: 3.然后可以根据 ...

  6. getCurrentPages

    解释:getCurrentPages 全局函数用于获取当前页面栈的实例,以数组形式按栈的顺序给出,第一个元素为首页,最后一个元素为当前页面. 示例: // index.jsPage({ onShow( ...

  7. [USACO08FEB]Hotel 题解

    正确的题解 首先我们都知道这题要用线段树做.考虑维护靠左边的answer,靠右边的answer,和整个区间的answer,那么就珂以维护这道题目了. 这里比较复杂的有下传操作和上传操作. 上传 voi ...

  8. [CF535D]Tavas and Malekas 题解

    题意简述 有一个空着的长度为\(n\)的字符串\(ans\),再给出一个长度为\(m\)的序列\(a\),现要在序列中每个元素\(a_i\)的位置开始都规定必须为一个给定的字符串\(s\).问字符串\ ...

  9. E. Compress Words

    E. Compress Words KMP #include<bits/stdc++.h> using namespace std; ]; int len; void getNext(ch ...

  10. Cocoapods 版本

    查看当前安装的版本 gem list 卸载版本 gem uninstall cocoapods 安装 gem install cocoapods gem install cocoapods -v 1. ...