
Java高并发——“锁”知识整理
发布日期:2021-05-14 09:31:32
浏览次数:20
分类:精选文章
本文共 2158 字,大约阅读时间需要 7 分钟。
最近在复习Java并发的时候,着重看了各类锁,了解了基本概念,现在将相关锁的知识点整理如下,供初学者快速入门理解。
导航
乐观锁和悲观锁
- 乐观锁:在并发下对数据进行修改时保持乐观的态度。认为在自己修改数据的过程中,其它线程不会对同一数据进行修改,所以不对数据加锁,但是在最终更新数据前,会判断一下这个数据有没有被修改,若没有,才将它更新为自己修改的值;
- 悲观锁:在并发下对数据进行修改时保持悲观的态度。认为在自己修改数据的过程中,其它线程也会对同一数据进行修改,所以在操作前先对数据加锁,在操作完成后释放锁,而在释放锁之前,其它线程无法操作该数据。
CAS是乐观锁的一种实现方式,而悲观锁比较典型的是synchronized。
独占锁和共享锁
- 独占锁:又叫排它锁,指该锁只能被一个线程所拥有。如果线程T对数据A加上独占锁后,其它线程不能再对A加任何锁。获得排它锁的线程既能读数据又能修改数据。 ReentrantLock 和 synchronized 以及JUC中的Lock都是独占锁
- 共享锁:指该锁可被多个线程共享。如果线程T对数据A加上共享锁后,其它线程只能对A再加共享锁,不能加排它锁。获得共享锁的线程只能读数据,不能修改数据。
独占锁和共享锁都是通过AQS实现的;
ReentrantReadWriteLock:读锁(ReadLock)是共享锁,写锁(WriteLock)是独占锁。二者都是通过内部类Sync(AQS的一个子类)实现的锁。读锁的共享可以保证高并发性。公平锁和非公平锁
- 公平锁:多个线程会按照申请顺序去获得锁,线程直接进入队列去排队,永远都是队列的第一位才能获得锁。
- 优点:所有线程都能获得资源,不会饿死在队列中;
- 缺点:吞吐量会下降很多,除了第一个线程,其它线程都会阻塞,CPU唤醒线程的开销会很大。
- 非公平锁:多个线程去获取锁时,获取不到再进入队列等待,如果能获取到,直接获取。
- 优点:CPU不必唤醒所有线程,减少了CPU唤醒线程的开销,整体的吞吐率会上升;
- 缺点:队列中的一些线程会因为长时间获取不到锁而饿死。
synchronized同步锁
有关synchronized同步锁在我之前的一篇博客里已经讲过,这里抛一下链接:
锁升级
锁一共有四种状态:无锁、偏向锁、轻量级锁和重量级锁(逐渐升级)。锁的优缺点对比:
锁 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
偏向锁 | 加锁和解锁不需要额外消耗,与非同步锁方法仅存在微小差距 | 如果线程之间存在锁竞争,会带来额外的锁撤销的消耗 | 单线程访问同步块 |
轻量级锁 | 竞争的线程不会产生阻塞,提高了程序响应速度 | 始终得不到锁的线程自旋会消耗CPU | 追求响应时间,同步块执行速度非常快 |
重量级锁 | 线程竞争不使用自旋消耗CPU | 线程阻塞,响应时间缓慢 | 追求吞吐量,同步块执行时间较长 |
JVM锁优化
锁优化这块写的比较糙,主要参考的是这个博客:,后期认真看完其它讲解后会补充详细~
自旋锁与自适应自旋
传统的独占锁会产生阻塞,线程的挂起和恢复需要转入内核态中完成,非常影响效率。
- 自旋锁:多线程场景下,线程之间通过抢占CPU时间分片执行任务,有可能持有锁的线程很快就会释放锁,此时让后面的线程执行一个忙循环进行等待,这就是自旋锁。自旋次数默认为10次,可使用-XX:PreblockSpin参数来更改
- JDK1.6加入了自适应自旋锁,这意味着自旋时间不再是固定的,而是由上一次在同一个锁上的自旋时间及锁的拥有者状态来决定。
锁消除
锁消除是指在JVM即时编译器运行时,在对代码进行同步时,对检测到不存在共享数据竞争的锁进行消除。
锁粗化
如果一系列的连续操作都对同一个对象反复加锁和解锁,则将会把同步的范围粗化到整个操作序列的外部,这样只需要加一次锁就好了。
偏向锁
- 目的:在无竞争的情况下消除整个同步使用的互斥量,连CAS操作都不做了。
- 定义:偏向锁会偏向第一个获得它的线程,如果在接下来的执行过程中该锁没有被其它线程获取,则持有偏向锁的线程以后进入这个锁相关的同步块不再需要进行同步。
- 原理: 1.锁对象第一次被获取时,虚拟机会把对象头中的锁标志位设置为01,即偏向锁模式; 2.同时使用CAS操作将线程ID记录在对象的 Mark World 之中; 3.若CAS操作成功,则以后进入这个锁相关的同步块时不再进行任何同步操作。
当另一个线程尝试获取这个锁时,偏向模式结束。此时根据锁对象是否处于锁定状态,将锁对象恢复到未锁定状态或者轻量级锁状态(锁的升级,单向不可逆)。
轻量级锁
- 目的:在无竞争的情况下使用CAS操作去消除同步使用的互斥量;
- 原理: 1.代码进入同步块的时候,若锁对象没有被锁定。 2.虚拟机首先在栈帧中建立一个名为锁记录(Lock Record)的空间,存储锁对象的 Mark World 的拷贝; 3.虚拟机使用CAS操作将对象的 Mark World 更新为指向 Lock Record 的指针; 4.若这个CAS操作成功,则此对象处于轻量级锁定状态; 5.若这个CAS操作失败,则检查对象的 Mark World 是否已经指向当前线程栈帧中的 Lock Record, 6.若已经指向,则表明当前线程拥有该对象的锁,继续执行,否则膨胀为重量级锁。
发表评论
最新留言
网站不错 人气很旺了 加油
[***.192.178.218]2025年05月03日 11时23分51秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
wxWidgets源码分析(5) - 窗口管理
2021-05-09
wxWidgets源码分析(7) - 窗口尺寸
2021-05-09
wxWidgets源码分析(8) - MVC架构
2021-05-09
wxWidgets源码分析(9) - wxString
2021-05-09
Mybatis Generator最完整配置详解
2021-05-09
[白话解析] 深入浅出熵的概念 & 决策树之ID3算法
2021-05-09
[梁山好汉说IT] 梁山好汉和抢劫银行
2021-05-09
[源码解析] 消息队列 Kombu 之 基本架构
2021-05-09
[源码分析] 消息队列 Kombu 之 启动过程
2021-05-09
[源码分析] 消息队列 Kombu 之 Consumer
2021-05-09
抉择之苦
2021-05-09
wx.NET CLI wrapper for wxWidgets
2021-05-09
ASP.NET MVC Action Filters
2021-05-09
Powershell中禁止执行脚本解决办法
2021-05-09
HTTP协议状态码详解(HTTP Status Code)
2021-05-09
OO_Unit2 多线程电梯总结
2021-05-09
04_Mysql配置文件(重要参数)
2021-05-09