Lock、Synchronized锁区别解析( 二 )
public void aa(){synchronized (this){int gg = 4;}}public void bb(){int gg = 4;}在使用 jclass Bytecode viewer 工具将编译后的 class 文件转成可视化的指令集后 , 可以看到指令集如下:
aa 方法:
文章插图
bb 方法:
文章插图
可以看到在 aa 方法中多了一些指令 , 其中比较重要的就是 "monitorenter"、"monitorexit" 。 这两个指令对应的着 "解锁" 和 "加锁", 也就是线程 "获取到锁" 以及代码执行完成后的 "释放锁" 。 "monitor" 就对应着文章开头说的监视器 , 因为 synchronized 就是 java 中的 "监视器" 。 指令中有一次 "monitorenter" 代表线程得到 CPU 调度(也叫做线程得到了锁) , 但是指令里却有两次 "monitorexit" , 这是为了防止线程发生异常没有执行 第一次的 "monitorexit" , 而导致其他线程永远无法得到线程 。
Synchronized 锁升级机制在 JDK 早期的版本 , synchronized 锁的效率是非常低的 , 它的效率远低于 lock 锁 , 但是 sychronized 毕竟是 java 的关键词 , 它不应该就此淘汰 。 所以在 JDK1.6 中对它进行优化 , 其实优化内容不仅仅是与 synchronized 有关的 , 还有 "自适应自旋锁"、"锁消除"、"锁粗化" 等 。 这些优化和各种锁以及多线程的其他知识后面会开单独的一篇多线程的专栏来说 。 这里先简单的提一下 , 关于 synchronized 的优化就是它的升级机制 。 synchronized 也因为这个优化效率变得能和 Lock 锁效率不相上下 。
1.6 之前的 synchronized 都是 "重量级锁" , 什么是重量级锁呢?就是一个线程在获取到 CPU 调度后 , 开始执行 synchronized 修饰的代码块 , 这时其他执行到这里的线程必须进行一次 " 上下文切换 "(下面有解释)(其实在进行上下文切换前会先尝试获取锁资源 , 失败才会进行"上下文切换" , 这是非公平锁的特性 , 下面 Lock 部分有讲解 , 这里比较的是 synchronized 效率问题 , 所以忽略一开始就抢夺到锁资源的情况)和 "加锁 "、"解锁" 操作 。 这就是 "重量级锁" , 这样的锁有严重的弊端 。 "上下文切换" 和 "加锁"、 "解锁" 这些动作虽然在单线程下消耗的时间并不算多 , 但是在一些高并发场景 , 例如百万、千万并发的场景 , 那么这些动作消耗的总时间就比较大了;另外一种情况就是某段代码可能发生多个线程抢占执行的情况 , 但是实际并没有发生这种情况 , 都是一个线程执行完后下一个线程才执行到这段代码 , 这样 "加锁"、解锁" 消耗的时间就浪费了 。 那么有什么方法去解决这个问题呢 , 这就是锁升级机制带来的好处 。
上下文切换:线程之间的切换需要前一个线程先保存当前的状态 , 然后进入 "睡眠" 状态 , 然后下一个线程 "启动" , 执行 , 等到下一次前一个线程获取到 CPU 调度时 , 再去读取上次保存的状态 , 然后 "启动" 。 我们把一个线程从保存当前状态到下一次"启动"完成称作这个线程的一次 "上下文切换" 。
synchronized 锁升级机制是从偏向锁->轻量级锁->重量级锁, 这个过程是不可逆的 。
在具体说这三种锁时 , 先要了解对象头的 Mark Word 部分 , 我们都知道对象上存储着这个对象的一切信息 , 包括它的地址、内部方法、属性等信息 , 前面说过监视器 , 就是一个锁对应着一个对象 , 所以在对象上也存储着这个对象所关联锁的信息 。 关于锁的信息就存储在对象对象头的 Mark Word 部分上 。 下面是 Mark Word 结构示意图:
文章插图
下面说得偏向锁、轻量级锁、重量级锁都会用到这上面的字段 。
1、偏向锁
文章插图
首先是偏向锁 , 偏向锁是指一段代码同一时间内只有一个线程执行(这是在开启了重偏向 , 如果没有开启重偏向则是一段代码一直只有一个线程执行) 。 当不满足条件时就会升级成轻量级锁 。 偏向锁的执行逻辑是:
1、 判断 对象头的 Mark Word 部分的锁标志位 ,01表示为偏向锁 , 00轻量级锁 , 10重量级锁
2、 判断是否偏向锁
1、0 , 升级为轻量级锁 , 然后执行相关策略
2、1 , 检查线程ID位是否是当前线程ID 。
1、 是 , 获得偏向锁 , 执行代码
2、 否 , 尝试进行 CAS写入当前线程ID
1、 成功 。 获得锁 , 执行
2、 失败 。 说明已经存在线程 ID了 , 会在安全的时间点暂停当前持有该偏向锁的线程 , 然后判断该线程是否存活
- 空调|让格力、海尔都担忧,中国取暖“新潮物”强势来袭,空调将成闲置品?
- 占营收|华为值多少钱
- 俄罗斯手机市场|被三星、小米击败,华为手机在俄罗斯排名跌至第三!
- 页面|如何简单、快速制作流程图?上班族的画图技巧get
- 操盘|中兴统一操盘中兴、努比亚、红魔三大品牌
- 印度|拒绝华为后,印度、英国斥资数十亿求助日本
- 华为|台积电、高通、华为、小米接连宣布!美科技界炸锅:怎么会这样!
- 拍照|iPhone12还没捂热13就曝光了,屏幕、信号、拍照均有升级!
- 路由器|家里无线网经常断网、网速慢怎么办?教你几个小窍门,轻松解决
- 一图看懂!数字日照、新型智慧城市这样建(上篇)|政策解读 | 新型
