温故知新-多线程-深入刨析synchronized
- Posted by 微博@Yangsc_o
- 原创文章,版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0
文章目录
摘要
本文从synchronized的基本用法,结合对象的内存布局,深入探究synchronized的锁升级的过程,探究其实现原理。准备开始~
synchronized的基础用法
修饰普通同步⽅方法
对于非static的情况,synchronized是对象级别的,其实质是将synchronized作用于对象引用(object reference)上,即拿到p1对象锁的线程,对的fun()方法有同步互斥作用,不同的对象之间坚持“和平共处”。
// 锁的对象是方法的调用者
public synchronized void method(){ }
上边的示例代码等同于如下代码:
public void method() {
synchronized (this) { }
}
修饰静态同步方法
如果方法用static修饰,synchronized的作用范围就是class一级的,它对类的所有对象起作用。
Class Foo{
public static synchronized void method1() {
}
}
上边的示例代码等同于如下代码:
public void method2()
{
synchronized(Foo.class) {
// class literal(类名称字面常量)
//请注意,Foo.class也是一个对象,类型是Class,在一个ClassLoader里,它是唯一的。
}
}修饰同步代码块
锁就是so这个对象,谁拿到这个锁谁就能够运行他所控制的那段代码。
同步块,示例代码如下:
public void method(SomeObject so) {
synchronized(so)
{
//…..
}
}
当有一个明确的对象作为锁时,就能够这样写代码,但当没有明确的对象作为锁,只是想让一段代码同步时,能够创建一个特别的instance变量(它得是个对象)来充当锁
class Foo implements Runnable{
private byte[] lock = new byte[0]; // 特别的instance变量
Public void method(){
synchronized(lock) { }
}
- 注:零长度的byte数组对象创建起来将比任何对象都经济。查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
特殊说明
- 使⽤用synchronized修饰类和对象时,由于类对象和实例例对象分别拥有⾃自⼰己的监视器器锁,因此不不会
相互阻塞 - 使⽤synchronized修饰实例例对象时,如果一个线程正在访问实例例对象的一个synchronized方法时,其它线程不仅不能访问该synchronized⽅法,该对象的其它synchronized方法也不不能访问,因为一个对象只有一个监视器器锁对象,但是其它线程可以访问该对象的非synchronized⽅法。
synchronized原理
字节码理解
在说synchronized原理时,就不得不先了解一下Monitor
认识 Java Monitor Object
Java Monitor 从两个方面来支持线程之间的同步,即:互斥执行(对象内的所有方法都互斥的执行。好比一个 Monitor 只有一个运行许可,任一个线程进入任何一个方法都需要获得这个许可,离开时把许可归还)与协作(通常提供signal机制:允许正持有许可的线程暂时放弃许可,等待某个监视条件成真,条件成立后,当前线程可以通知正在等待这个条件的线程,让它可以重新获得运行许可)。 Java 使用对象锁 ( 使用 synchronized 获得对象锁 ) 保证工作在共享的数据集上的线程互斥执行 , 使用 notify/notifyAll/wait 方法来协同不同线程之间的工作。这些方法在 Object 类上被定义,会被所有的 Java 对象自动继承。
Java 语言对于这样一个典型并发设计模式做了内建的支持,线程如果获得监视锁成功,将成为该监视者对象的拥有者。在任一时刻内,监视者对象只属于一个活动线程 (Owner) 。拥有者线程可以调用 wait 方法自动释放监视锁,进入等待状态。下图很好地描述了 Java Monitor 的工作机理。
在Java虚拟机(HotSpot)中, monitor是由ObjectMonitor实现的,其主要数据结构如下(位于HotSpot虚拟机源码ObjectMonitor.hpp⽂文件,C++实现的),位于/openjdk-8u60/hotspot/src/share/vm/runtime/objectMonitor.hpp
ObjectMonitor() {
_header = NULL; //markOop对象头
_count = 0; // 可以⼤大于1,可重⼊入
_waiters = 0, //等待线程数
_recursions = 0; //重⼊入次数
_object = NULL; //监视器器锁寄⽣生的对象。锁不不是平⽩白出现的,⽽而是寄托存储于对象中。
_owner = NULL; //初始时为NULL表示当前没有任何线程拥有该monitor record,当线程成功拥有该锁后保存线程唯⼀一标识,当锁被释放时⼜又设置为NULL
_WaitSet = NULL; //处于wait状态的线程,会被加⼊入到_WaitSet
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ; //处于等待锁block状态的线程,会被加⼊入到该列列表
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
_previous_owner_tid = 0;
}
每个线程都有两个ObjectMonitor对象列列表,分别为free和used列列表,如果当前free列列表为空,线程将
向全局global list请求分配ObjectMonitor。thread结构/openjdk-8u60/hotspot/src/share/vm/runtime/thread.hpp
// Private thread-local objectmonitor list - a simple cache organized as a SLL.
public:
ObjectMonitor* omFreeList;
int omFreeCount; // length of omFreeList
int omFreeProvision; // reload chunk size
ObjectMonitor* omInUseList; // SLL to track monitors in circulation
int omInUseCount; // length of omInUseList
ObjectMonitor中有两个队列列, _WaitSet 和 _EntryList,用来保存ObjectWaiter对象列列表( 每个等待锁的线程都会被封装成ObjectWaiter对象), owner指向持有ObjectMonitor对象的线程,当多个线程同时访问一段同步代码时,首先会进⼊入 EntryList 集合,当线程获取到对象的monitor 后进⼊ _Owner 区域并把monitor中的owner变量量设置为当前线程同时monitor中的计数器器count加1,若线程调⽤用 wait() ⽅法,将释放当前持有的monitor, owner变量量恢复为null, count⾃自减1,同时该线程进⼊入 WaitSet集合中等待被唤醒。若当前线程执⾏行行完毕也将释放monitor(锁)并复位变量量的值,以便便其他线程进⼊入获取monitor(锁)。
由此看来, monitor对象存在于每个Java对象的对象头中(存储的指针的指向), synchronized锁便是通过这种⽅式获取锁的,也是为什么Java中任意对象可以作为锁的原因。
代码验证
是不是一脸崩?不知道在说什么
温故知新-多线程-深入刨析synchronized的更多相关文章
- 温故知新-多线程-深入刨析volatile关键词
文章目录 摘要 volatile的作用 volatile如何解决线程可见? CPU Cache CPU Cache & 主内存 缓存一致性协议 volatile如何解决指令重排序? volat ...
- 温故知新-多线程-深入刨析CAS
文章目录 摘要 CAS是什么? CAS是如何实现的? CAS存在的问题? 你的鼓励也是我创作的动力 Posted by 微博@Yangsc_o 原创文章,版权声明:自由转载-非商用-非衍生-保持署名 ...
- 温故知新-多线程-深入刨析park、unpark
文章目录 摘要 park.unpark 看一下hotspot实现 参考 你的鼓励也是我创作的动力 Posted by 微博@Yangsc_o 原创文章,版权声明:自由转载-非商用-非衍生-保持署名 | ...
- 温故知新-多线程-Cache Line存在验证
文章目录 简述 缓存行Cache Line 验证CacehLine存在? 参考 你的鼓励也是我创作的动力 Posted by 微博@Yangsc_o 原创文章,版权声明:自由转载-非商用-非衍生-保持 ...
- 深入刨析tomcat 之---第8篇 how tomcat works 第11章 11.9应用程序,自定义Filter,及注册
writed by 张艳涛, 标签:全网独一份, 自定义一个Filter 起因:在学习深入刨析tomcat的学习中,第11章,说了调用过滤链的原理,但没有给出实例来,自己经过分析,给出来了一个Filt ...
- 深入刨析tomcat 之---第2篇,解决第3章bug 页面不显示内容http://localhost:8080/servlet/ModernServlet?userName=zhangyantao&password=1234
writedby 张艳涛7月2日, 在学习第4张的过程中,发现了前一篇文章写的是关于1,2张的bug不用设置response响应头,需要在servlet的service()方法里面写是错误想 serv ...
- Orchard 刨析:Logging
最近事情比较多,有预研的,有目前正在研发的,都是很需要时间的工作,所以导致这周只写了两篇Orchard系列的文章,这边不能保证后期会很频繁的更新该系列,但我会写完这整个系列,包括后面会把正在研发的东西 ...
- Orchard 刨析:Caching
关于Orchard中的Caching组件已经有一些文章做了介绍,为了系列的完整性会再次对Caching组件进行一次介绍. 缓存的使用 在Orchard看到如下一段代码: 可以看到使用缓存的方法Get而 ...
- Orchard 刨析:导航篇
之前承诺过针对Orchard Framework写一个系列.本应该在昨天写下这篇导航篇,不过昨天比较累偷懒的去玩了两盘单机游戏哈哈.下面进入正题. 写在前面 面向读者 之前和本文一再以Orchard ...
随机推荐
- ql的python学习之路-day3
字典操作 特性: 1.无序的 2.key是唯一的 , ,,], ,,], ,,], }, ,,], 'bbb' : ['a', 'b', 'c'], }}
- 学习ASP.NET Core(06)-Restful与WebAPI
上一篇我们使用Swagger添加了接口文档,使用Jwt完成了授权,本章我们简答介绍一下RESTful风格的WebAPI开发过程中涉及到的一些知识点,并完善一下尚未完成的功能 .NET下的WebAPI是 ...
- 关于字符串函数size()的问题
首先如果你是一段语句 for(int i=0;i<a.size();i++)这个时候会报错 comparison between signed and unsigned integer expr ...
- hdu6470 Count 矩阵快速幂
hdu6470 Count #include <bits/stdc++.h> using namespace std; typedef long long ll; , mod = ; st ...
- poj3764字典树路径最大异或和
The xor-longest Path Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 6853 Accepted: 1 ...
- MySQL 8.0用户及安全管理
用户的功能 登录数据库 管理数据库对象 用户的组成 用户名@'白名单' 白名单: % 10.0.0.10 .% 10.0.0.5% 10.0.0.0/255.255.254.0 oldguo.com ...
- 解决mysql:ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO/YES)
一.问题 有时候我们登录Mysql输入密码的时候,会出现这种情况 mysql -u root -p Enter Password > '密码' 错误:ERROR 1045 (28000): Ac ...
- HashMap集合嵌套集合方法四种
Map<String, HashMap<Person, String>> m=new HashMap<String, HashMap<Person, String& ...
- 剑指Offer之旋转数组的最小数字
题目描述 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素.例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转 ...
- vue中使用jsx
vue中使用jsx 为什么需要使用jsx呢?这个需要搞清楚 其实vue官方也说了,对于那些非常多v-if v-else的情况,就可以尝试使用render函数或者jsx,不过render函数写简单的结构 ...