jdk源码剖析:Synchronized
一、Synchronized作用
(1)确保线程互斥的访问同步代码
(2)保证共享变量的修改能够及时可见
(3)有效解决重排序问题。(Synchronized同步中的代码JVM不会轻易优化重排序)
二、Synchronized常见用法分析
1.修饰普通方法
package lock; /**
*
* @ClassName:SynchronizedDemo
* @Description:测试synchronized
* @author diandian.zhang
* @date 2017年4月1日下午7:02:34
*/
public class SynchronizedDemo { public synchronized void method1(){
System.out.println("进入方法1");
try {
System.out.println("方法1执行");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("方法1 end");
} public synchronized void method2(){
System.out.println("进入方法2");
try {
System.out.println("方法2执行");
Thread.sleep(000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("方法2 end");
} public static void main(String[] args) {
SynchronizedDemo demo = new SynchronizedDemo();
new Thread(new Runnable() {
@Override
public void run() {
demo.method1();
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
demo.method2();
}
}).start();
}
}
结果:
进入方法1
方法1执行
方法1 end
进入方法2
方法2执行
方法2 end
可见:修饰普通方法,线程2需要等待线程1的method1执行完成才能开始执行method2方法,方法级别串行执行。
2.修饰静态方法
package lock; /**
*
* @ClassName:SynchronizedDemo2
* @Description:修饰静态方法
* @author diandian.zhang
* @date 2017年4月5日上午10:48:56
*/
public class SynchronizedDemo2 { public static synchronized void method1(){
System.out.println("进入方法1");
try {
System.out.println("方法1执行");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("方法1 end");
} public static synchronized void method2(){
System.out.println("进入方法2");
try {
System.out.println("方法2执行");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("方法2 end");
} public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
SynchronizedDemo2.method1();
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
SynchronizedDemo2.method2();
}
}).start();
}
}
运行结果:
进入方法1
方法1执行
进入方法2
方法1 end
方法2执行
方法2 end
可见:修饰静态方法,本质是对类的同步,任何实例调用方法,都类级别串行(每个实例不一定串行)执行。
3.修饰代码块
package lock; /**
*
* @ClassName:SynchronizedDemo3
* @Description:Synchronized修饰代码块
* @author diandian.zhang
* @date 2017年4月5日上午10:49:50
*/
public class SynchronizedDemo3 { public void method1(){
System.out.println("进入方法1");
try {
synchronized (this) {
System.out.println("方法1执行");
Thread.sleep(3000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("方法1 end");
} public void method2(){
System.out.println("进入方法2");
try {
synchronized (this) {
System.out.println("方法2执行");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("方法2 end");
} public static void main(String[] args) {
SynchronizedDemo3 demo = new SynchronizedDemo3();
new Thread(new Runnable() {
@Override
public void run() {
demo.method1();
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
demo.method2();
}
}).start();
}
}
运行结果:
进入方法1
方法1执行
进入方法2
方法1 end
方法2执行
方法2 end
可见,修饰代码块,只锁住代码块的执行顺序。代码块级别串行。(例如上面的方法1和方法2没能串行,因为锁住的是同一个对象,但是同步代码块只包住了方法中的一部分)
三、Synchronized 原理
实际上,JVM只区分两种不同用法 1.修饰代码块 2.修饰方法。什么,你不信?好吧,上SE8规范:http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-3.html#jvms-3.14

上图中,红框框中说明了,1.monitorenter+monitorexit(上图中的onlyMe方法中同步代码块) 2.修饰方法
这一切都是规范说的,下面自测一下吧~
下面,我们通过JDK自带工具反编译,查看包含java字节码的指令。
3.1 synchronized修饰代码块
java源码如下:
 public class SynchronizedDemo {
     public void method (){
         synchronized (this) {
             System.out.println("method 1 start!!!!");
         }
     }
 }
javac -encoding utf-8 SynchronizedDemo.java 编译生成class 后,javap -c 反编译一下,看指令:

这里着重分析2个monitorenter、monitorexit这两个指令。这里以JSE8位为准,查到属于JVM指令集。官网各种API、JVM规范,指令等,传送门:http://docs.oracle.com/javase/8/docs/。
1.monitorenter监视器准入指令

主要看上图中Description:
每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:
1.如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。
2.如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.
3.’如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。
2.monitorexit监视器释放指令

主要看上图中Description:
1.执行monitorexit的线程必须是objectref所对应的monitor的所有者。
2.指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。
3.2 synchronized修饰方法
java源码如下:
package lock; /**
*
* @ClassName:SynchronizedDemo0
* @Description: Synchronized修饰方法
* @author diandian.zhang
* @date 2017年4月5日下午6:18:12
*/
public class SynchronizedDemo0 {
public synchronized void method (){
System.out.println("method start!!!!");
}
}
javap -v 查看class文件,发现method上加了同步标签,本质上还是monitor

========附言分割线=====
附:全文参考http://www.cnblogs.com/paddix/p/5367116.html
jdk源码剖析:Synchronized的更多相关文章
- jdk源码剖析三:锁Synchronized
		
一.Synchronized作用 (1)确保线程互斥的访问同步代码 (2)保证共享变量的修改能够及时可见 (3)有效解决重排序问题.(Synchronized同步中的代码JVM不会轻易优化重排序) 二 ...
 - jdk源码剖析二: 对象内存布局、synchronized终极原理
		
很多人一提到锁,自然第一个想到了synchronized,但一直不懂源码实现,现特地追踪到C++层来剥开synchronized的面纱. 网上的很多描述大都不全,让人看了不够爽,看完本章,你将彻底了解 ...
 - jdk源码剖析一:OpenJDK-Hotspot源码包目录结构
		
开启正文之前,先说一下源码剖析这一系列,就以“死磕到底”的精神贯彻始终,JDK-->JRE-->JVM(以openJDK代替) 最近想看看JDK8源码,但JDK中JVM(安装在本地C:\P ...
 - jdk源码剖析四:JDK1.7升级1.8 HashMap原理的变化
		
一.hashMap数据结构 如上图所示,JDK7之前hashmap又叫散列链表:基于一个数组以及多个链表的实现,hash值冲突的时候,就将对应节点以链表的形式存储. JDK8中,当同一个hash值(T ...
 - 1.1 jvm核心类加载器--jdk源码剖析
		
目录 前提: 运行环境 1. 类加载的过程 1.1 类加载器初始化的过程 1.2 类加载的过程 1.3 类的懒加载 2. jvm核心类加载器 3. 双亲委派机制 4. 自定义类加载器 5. tomca ...
 - jdk源码剖析五:JDK8-废弃永久代(PermGen)迎来元空间(Metaspace)
		
目录 1.背景 2.为什么废弃永久代(PermGen) 3.深入理解元空间(Metaspace) 4.总结 ========正文分割线===== 一.背景 1.1 永久代(PermGen)在哪里? 根 ...
 - 【并发编程】【JDK源码】CAS与synchronized
		
线程安全 众所周知,Java是多线程的.但是,Java对多线程的支持其实是一把双刃剑.一旦涉及到多个线程操作共享资源的情况时,处理不好就可能产生线程安全问题.线程安全性可能是非常复杂的,在没有充足的同 ...
 - Thread类源码剖析
		
目录 1.引子 2.JVM线程状态 3.Thread常用方法 4.拓展点 一.引子 说来也有些汗颜,搞了几年java,忽然发现竟然没拜读过java.lang.Thread类源码,这次特地拿出来晒一晒. ...
 - 转:【Java集合源码剖析】Hashtable源码剖析
		
转载请注明出处:http://blog.csdn.net/ns_code/article/details/36191279 Hashtable简介 Hashtable同样是基于哈希表实现的,同样每个元 ...
 
随机推荐
- sublime与Emment
			
sublime与Emment 作为一个开发者,想必用过sublime和Emment 的无不大快朵颐,这两者结合在一起简直是天合之作.它不仅仅提高编码的速度而且令开发者感到编码的乐趣和舒适感,今天准备写 ...
 - BeautifulSoup简述
			
网页解析器 从网页中提取有价值数据的工具 网页解析器种类 正则表达式 (模糊匹配) html.parser (结构化解析) BeautifulSoup第三方插件 (结构化解析,相对比较强大) lxml ...
 - Linux Tomcat安装,Linux配置Tomcat,Linux Tomcat修改内存,Linux tomcat修改端口
			
Linux Tomcat安装,Linux配置Tomcat,Linux Tomcat修改内存,Linux tomcat修改端口 >>>>>>>>>& ...
 - 301、404、200、304、500HTTP状态
			
一些常见的状态码为: 200 - 服务器成功返回网页 404 - 请求的网页不存在 503 - 服务器超时 下面提供 HTTP 状态码的完整列表.点击链接可了解详情.您也可以访问 HTTP 状态码上的 ...
 - 用 node.js 创建第一个Hello World
			
如果我们使用PHP来编写后端的代码时,需要Apache(xampp) 或者 Nginx 的HTTP 服务器,并配上 mod_php5 模块和php-cgi.从这个角度看,整个"接收 HTTP ...
 - Mditor 发布「桌面版」了 - http://mditor.com
			
简单说明 Mditor 最早只有「组件版」,随着「桌面版」的发布,Mditor 目前有两个版本: 可嵌入到任意 Web 应用的 Embed 版本,这是一桌面版的基础,Repo: https://git ...
 - 2729: [HNOI2012]排队
			
2729: [HNOI2012]排队 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 957 Solved: 449[Submit][Status] ...
 - Java反射机制深度剖析
			
版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! Java反射机制是Java语言中一种很重要的机制,可能在工作中用到的机会不多,但是在很多框架中都有用到这种机制.我们知道Java是一门静态 ...
 - servlet匹配规则和顺序
			
1. 写法 ①完全匹配:以“/”开头,以字母(非“*”)结束 如:<url-pattern>/test/list.do</url-pattern> ②目录匹配:以“/” ...
 - HTML <form> 标签的 method 属性(20161028)
			
HTML <form> 标签的 method 属性 HTML <form> 标签 实例 在下面的例子中,表单数据将通过 method 属性附加到 URL 上: <form ...