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
开启正文之前,先说一下源码剖析这一系列,就以"死磕到底"的精神贯彻始终,最少追踪到JVM指令(再往下C语言实现了). =========正文分割线=========== Sync ...
- 一点一点看JDK源码(三)java.util.ArrayList 前偏
一点一点看JDK源码(三)java.util.ArrayList liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) 1.综述 ArrayLi ...
- 【并发编程】【JDK源码】CAS与synchronized
线程安全 众所周知,Java是多线程的.但是,Java对多线程的支持其实是一把双刃剑.一旦涉及到多个线程操作共享资源的情况时,处理不好就可能产生线程安全问题.线程安全性可能是非常复杂的,在没有充足的同 ...
- jdk源码剖析一:OpenJDK-Hotspot源码包目录结构
开启正文之前,先说一下源码剖析这一系列,就以“死磕到底”的精神贯彻始终,JDK-->JRE-->JVM(以openJDK代替) 最近想看看JDK8源码,但JDK中JVM(安装在本地C:\P ...
- Django Rest Framework源码剖析(三)-----频率控制
一.简介 承接上篇文章Django Rest Framework源码剖析(二)-----权限,当服务的接口被频繁调用,导致资源紧张怎么办呢?当然或许有很多解决办法,比如:负载均衡.提高服务器配置.通过 ...
- (文字版)Qt信号槽源码剖析(三)
大家好,我是IT文艺男,来自一线大厂的一线程序员 上节视频给大家讲解了Qt信号槽的Qt宏展开推导:今天接着深入分析,进入Qt信号槽源码剖析系列的第三节视频. Qt信号槽宏推导归纳 #define si ...
- Dubbo源码剖析三之服务注册过程分析
Dubbo源码剖析二之注册中心 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中对注册中心进行了简单的介绍,对Dubbo整合Zookeeper链接源码进行了详细分析.本文接着对服务注册过 ...
- jdk源码剖析二: 对象内存布局、synchronized终极原理
很多人一提到锁,自然第一个想到了synchronized,但一直不懂源码实现,现特地追踪到C++层来剥开synchronized的面纱. 网上的很多描述大都不全,让人看了不够爽,看完本章,你将彻底了解 ...
- jdk源码剖析四:JDK1.7升级1.8 HashMap原理的变化
一.hashMap数据结构 如上图所示,JDK7之前hashmap又叫散列链表:基于一个数组以及多个链表的实现,hash值冲突的时候,就将对应节点以链表的形式存储. JDK8中,当同一个hash值(T ...
随机推荐
- day 50 JS框架基础
一 JavaScript的历史1 Netscape(网景)接收Nombas的理念,(Brendan Eich)在其Netscape Navigator 2.0产品中开发出一套livescript的脚本 ...
- [JetBrains注册] 利用教育邮箱注册JetBrains产品(pycharm、idea等)的方法
我们在使用JetBrains的一些产品时,大多使用网上的一些key去注册或者pojie的,但是由于提供这些key的服务器并不能保证稳定可用,所以可能一段时间我们使用的ide又需要重新pojie. 这里 ...
- Enhancement in SAP abap.
Recently I have been taught through how to do enhancement for those standard programs. Th reason for ...
- SpringBatch Sample (四)(固定长格式文件读写)
前篇关于Spring Batch的文章,讲述了Spring Batch 对XML文件的读写操作. 本文将通过一个完整的实例,与大家一起讨论运用Spring Batch对固定长格式文件的读写操作.实例延 ...
- Android Hook框架adbi源码浅析(二)
二.libbase 其实上面加载完SO库后,hook的功能我们完全可以自己在动态库中实现.而adbi作者为了方便我们使用,编写了一个通用的hook框架工具即libbase库.libbase依然在解决两 ...
- Java中的break和continue以及标签
一.Java中的break,continue,goto 首先break,continue是Java中的关键字,而goto是保留字. 基于goto在c和c++中的鬼畜表现,我觉得goto可能还会长期在J ...
- Python用re正则化模块在字符串查找特定字符串
实验需要,在一个含有几亿个字符的txt文件中查找特定的字符串,首先用re模块进行查找 from time import clock import re start=clock() label_file ...
- Echarts 绘图(饼图,中国地图)
一个使用Javascript 实现的开源可视化库,可以流畅的运行在pc 和移动设备上,兼容当前绝大部分浏览器(Chrome ,firefox,IE8)等 底层依赖轻量级的矢量图形库ZRender,提供 ...
- sed 等相关的复习
sed相打印两行之间的内容: sed -n '/111/,/aad/p' fuxi.txt grep -n ".*" fuxi.txt sed -n '2,9'p fuxi.txt ...
- Tree Recovery
#include<stdio.h> #include<string.h> void build(int n,char*s1,char*s2) { )return ; ])-s2 ...