1. 线程锁:synchronized简单了解
1.1 基本使用
特点:获取的锁是唯一
特点2: 持有锁的线程,执行完代码块,放锁,其他线程才能操作代码块里的公共资源。
ps: 在有线程持有锁的时候,其他线程也会去尝试拿锁,但是会失败自旋
核心:避免做事做一半发生上下文切换导致公共资源数据变脏

1.2 锁对象的区别
ps:synchronized只能锁对象
容易混淆


1.3 常见线程不安全使用方式
1.3.1 多线程调用多个方法 多个方法处理共享资源
不安全:

1.3.2 多线程调用多个方法 每个方法有自己的栈帧

ps:final修饰方法设计程序时,若希望一个方法的行为在继承期间保持不变,而且不可被覆盖或改写,就可以采取这种做法。
采用final方法的第二个理由是程序执行的效率。将一个方法设成final后,编译器就可以把对那个方法的所有调用都置入“嵌入”调用里。只要编译器发现一个final方法调用,就会(根据它自己的判断)忽略为执行方法调用机制而采取的常规代码插入方法(将自变量压入堆栈;跳至方法代码并执行它;跳回来;清除堆栈自变量;最后对返回值进行处理)。
ps:private 等于也是被final修饰 外界无法访问和重写
1.3.3 多线程调用多个方法 方法中创建新的线程来和自己处理同一个对象

1.3.4 AOP中使用成员变量

1.3.5 单例中操作成员变量

ps:因为servlet单例 导致service中 new dao也只会运行一次 也是单例
2. 线程锁:synchronized分析
2.1 基本知识
2.1.1 对象头的结构
快看看你对象头的结构


对象头中mark word的内容

2.2 Monitor(锁)
2.2.1 synchronized(obj) 的基本原理

对应字节码

2.2 java 6之后锁的优化
2.2.1 前言:为什么要优化
先聊聊为啥要锁优化和锁升级
一个场景:为了要避免出问题 一个房间里面同一个时间只能让一个人用
(i)为了不让别人进来,肯定也就要锁上门。但是发现业务场景里面,一个人进去干活2秒,开门锁门花了两分钟,哎呀我草(一种植物),锁门(重量级锁调用操作系统monitor的锁)也很费时间,要拿钥匙输密码之类的 不行不行
优化:那我们君子协定,在门口挂个牌(轻量级锁),我写上我名字的时候,你不能进来,在门口蹲着等(自旋锁),等我走之后我擦干净我的牌,我很快的你不用着急

适用于大部分场景是,有人用了房间很快就出来了
(ii)嗯 这样就很舒服 不用输密码了 但是有时候压根就没有人和我抢房间 我每次进去写个牌 出来擦干净 感觉自己就是个傻x 自己和自己斗智斗勇
优化:那就这样,我在房间上写上名字,爷不擦了(偏向锁) 反正每天也是我自己用
(iii)直到那一天... 有个老哥过来了,也要用房间,和我干了一架,你是爷啊占着茅坑不拉屎呢?
优化:管理员发现吵架了, 就说话了,换成君子协定吧(锁升级 偏向锁->轻量级锁)

(iiii)又有一天,老板过来了,定神一看,矮油卧槽,这门口蹲了七八个人全在等开门(大量线程自旋 cpu空转),这不是摸鱼嘛! 这可不行,我年底还想换辆劳斯莱斯的,可不能让他们这样。
优化:老板下令,换上密码锁(锁升级 轻量级锁->重量级锁)看到门关着,就都给我散了吧 没事就去等着,锁开了会叫你们
2.2.3 轻量级锁(“君子协定”)
每一个用synchronized锁住的对象 都会在对象头里面有锁信息 (ps:也就是门上的牌)
在栈帧里面发现锁之后,会有锁记录,然后会和对象的头信息进行比较和交换(CAS)
来实现轻量级锁

如果出现了多个人竞争这个房间,达到设置的条件,如自旋达到了一定次数,等的人等太久了,锁就会升级
在释放锁的时候也可能会出现特殊情况


ps:还是同一个人,自己的老婆进来慰问一下自己,不影响里面人的操作,释放锁的时候清除一下老婆进门的记录,保护自己的隐私 嘿嘿
专业术语:自己人进来叫锁重入
2.2.3 锁升级 轻量级锁->重量级锁(monitor)
当新的线程请求锁 进行CAS失败 多次自旋无果 则会申请要一个monitor锁
进行锁升级,那么 轻量级锁->重量级锁

2.2.4 自旋锁
因为当线程从阻塞状态-》就绪状态 需要从用户态切换成内核态 是一个大动作 比较消耗性能
可以想象成睡着了 醒来之后要调整调整才能工作
所以我们要经历避免 多次切换 让打工人能有机会打工 就赶快打工 实在是没事 再放他回去睡觉

2.2.5 偏向锁(“写上名字,爷不擦了”)
因为轻量级锁会出现多次CAS ,锁重入(老婆来探班,还要记录一下,过分)
明明只有一个线程进行操作,搞得花里胡哨的。

记了一次,就再也不记了 省的麻烦
2.2.6 对象头mark word复习
对象分为 对象头head,对象体body
对象体里面放我们自定义的东西
对象头里面放mark word 和 类信息(指向这个对象是什么类)
mark word 里面放hashcode,锁信息等

2.2.7 偏向锁撤销
1.由上图可知 偏向锁中 要存线程id 54位 没地方存hashcode
所以在执行中,调用hashcode方法 ,会将偏向锁撤销,转换为不可偏向状态。

2.新线程来调用这个对象 触发锁升级 偏向锁撤销 变成轻量级
3.调用wait notify等重量级锁的方法 偏向锁也会被撤销 变成重量级
2.2.7 偏向锁批量重偏向
也比较好理解,当某个人连续进入了30个房间,会在30个房间门上全部写上自己的名字(偏向锁)
当第二个人过来连续进入了20个房间,上帝之手来了一波预判,直接把剩下的房间写上第二个人的名字(偏向锁批量重偏向) ,就不用那个人再写了
JVM的自己的优化,线程1偏向完,线程二也开始逐个访问,阈值为20 到第二十个锁对象时,一开始的偏向则会更改为线程2
感谢:
《Java并发编程的艺术》方腾飞,魏鹏,程晓明
《Java并发实现原理:JDK源码剖析》余春龙
全面深入学习java并发编程,java基础进阶中级必会教程_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili
网友评论