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同样是基于哈希表实现的,同样每个元 ...
随机推荐
- BZOJ 1834: [ZJOI2010]network 网络扩容(网络流+费用流)
一看就知道是模板题= = ,不说什么了= = PS:回去搞期末了,暑假再来刷题了 CODE: #include<cstdio> #include<iostream> #incl ...
- 阿里云LINUX服务器配置HTTPS(NGINX)
本文首发于:http://www.fengzheng.pub/archives/238.html 背景说明 服务器为阿里云 ECS,操作系统为 CentOS 6.5. 部署配置说明 第一步,安装ngi ...
- 用C++编一程序,先输出一行sun mon tue wed thu fri fri,接着使用右对齐打印出日期,像日历那样
用C++编一程序,先输出一行sun mon tue wed thu fri fri,接着使用右对齐打印出日期,像日历那样 先输出一行sun mon tue wed thu fri fri,再提醒用户输 ...
- JS特效——图片水平滚动
具体源码如下: <!doctype html> <html lang="en"> <head> <meta http-equiv=&quo ...
- 【转】CXF+Spring+Eclipse简明示例
多系统(异构系统)进行交互时,一种良好的方式便是调用Web Service,本示例基于Apache组织的CXF,为了方便起见特将服务端和客户端写在同一个工程下,实际项目中是不可能的,但是客户端却依赖于 ...
- 【Java基础】String StringBuffer StringBuilder
String String是不可变的 我们都知道String不是基本数据类型,而是一个对象,并且是final类型的,不可变的.(public final class String) 查看以下代码: S ...
- 【Scala】Scala之Object
一.前言 前面学习了Scala的Methods,接着学习Scala中的Object 二.Object Object在Scala有两种含义,在Java中,其代表一个类的实例,而在Scala中,其还是一个 ...
- Asp.Net MVC学习总结(三)——过滤器你怎么看?
一.过滤器简介 1.1.理解什么是过滤器 1.过滤器(Filters)就是向请求处理管道中注入额外的逻辑.提供了一个简单而优雅的方式来实现横切关注点. 2.所谓的过滤器(Filters),MVC框架里 ...
- 微信小程序怎样提高应用速度小技巧
作者:vicyao, 腾讯web前端开发 高级工程师商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处. 原文链接:http://wetest.qq.com/lab/view/294.htm ...
- wemall doraemon中Android app商城系统解决左侧抽屉菜单和viewpager不能兼容问题
完美解决左侧抽屉菜单和viewpager不能兼容左右滑动的问题,可进行参考. WeMall-Client/res/layout/wemall_main_ui.xml </RadioGroup&g ...