1.Moment.js、Day.js、Miment,日期时间库怎么选?2.Parallax.js:让智能设备视差效果更智能、更自然3.Lottie动画全攻略:硬核还原100%页面动画效果4.用particles.js,让你的网站背景特效瞬间脱颖而出,惊艳所有人5.字节跳动出大招!IconPark图标库,自定义图标,好用到停不下来!6.比肩Element和Ant Design,PrimeVue同样优秀,你不可不知的UI框架新星!7.ChartCube图表快速上手指南,轻松打造专业图表,简单到不可思议!8.Vue地图开发新利器:Vue Baidu Map,轻松对接,效率翻倍!9.尤雨溪都在推荐的Naive UI,Vue组件库的新选择,好用到爆!10.【uni-app必备】uView UI框架,多端开发神器,让开发更简单、更高效!11.Driver.js:轻量级用户引导插件,小而美的界面,让用户体验飞起来!12.Ionicons图标库: 让网页栩栩生辉,Ionic Framework的经典之作,图标库新标杆!13.Clipboard.js:一个被157317个项目疯抢的JS开源库
14.探“锁”源头:synchronized、偏向锁与锁膨胀的秘密!
15.解"锁"疑惑:偏向锁为什么不是锁?锁升级又是什么?何时禁用偏向锁和轻量级锁?重量级锁怎么回事?16.秒懂!5分钟图解 Elasticsearch 搜索原理,快速掌握全文检索技术!17.2025年GitHub Copilot免费激活,周年庆典福利大放送!18.【深度解析】DDD领域驱动设计,分层架构秘籍大公开!让你的设计更上一层楼!19.AI编程工具怎么选?GitHub Copilot、AI Assistant与Cursor,谁是你的最佳拍档?20.【免费福利】腾讯云+DeepSeek:DeepSeek-V3/R1免费,羊毛党速来大家好,我是程序视点的小二哥!今天我们来聊聊Java中的锁!
-
synchronized怎么用?
-
锁是什么?
-
偏向锁是什么?
-
锁如何升级?何为膨胀?
-
自旋锁何解?
-
互斥锁怎么来的?
-
何时要禁用偏向锁和轻量级锁?
带着上面疑问,我们一起来解“锁”疑惑!以上问题会分成几篇文章来讲,方便大家记忆!欢迎持续关注【程序视点】,这样就不会错过之后的精彩内容啦!
前言
每一个刚接触多线程并发编程的同学,当被问到,如果多个线程同时访问一段代码,发生并发的时候,应该怎么处理?

我相信闪现在脑海中的第一个解决方案就是用synchronized,让这段代码同一时间只能被一个线程执行。
synchronized的疑惑
我们也知道,synchronized关键字可以用在方法上,也可以用在代码块上,如果要使用synchronized,我们一般就会如下使用:
public synchronized void doSomething() { //do something here}
或者
synchronized(LockObject) { //do something here}
那么实际上,synchronized关键字到底是怎么加锁的?锁又长什么样子的呢?
关于锁,还有一些什么样的概念需要我们去认识,去学习,去理解的呢?
以前在学习synchronized的时候,就有文章说, synchronized是一个很重的操作,开销很大,不要轻易使用,我们接受了这样的观点,但是为什么说是重的操作呢,为什么开销就大呢?
到java1.6之后,java的开发人员又针对锁机制实现了一些优化,又有文章告诉我们现在经过优化后,使用synchronized并没有什么太大的问题了,那这又是因为什么原因呢?到底是做了什么优化?
那今天我们就尝试着从锁机制实现的角度,来讲述一下synchronized在java虚拟机上面的适应场景是怎么样的。
由于java在1.6之后,引入了一些优化的方案,所以我们讲述synchronized,也会基于java1.6之后的版本。
锁对象
首先,我们要知道锁其实就是一个对象,java中每一个对象都能够作为锁。 所以我们在使用synchronized的时候,
-
对于同步代码块,就得指定锁对象。
-
对于修饰方法的synchronized,默认的锁对象就是当前方法的对象。
-
对于修饰静态方法的synchronized,其锁对象就是此方法所对应的类Class对象。
我们知道,所谓的对象,无非也就是内存上的一段地址,上面存放着对应的数据,那么我们就要想,作为锁,它跟其它的对象有什么不一样呢?怎么知道这个对象就是锁呢?怎么知道它跟哪个线程关联呢?它又怎么能够控制线程对于同步代码块的访问呢?
Markword
可以了解到在虚拟机中,对象在内存中的存储分为三部分:
-
对象头
-
实例数据
-
对齐填充
其中,对象头填充的是该对象的一些运行时数据,虚拟机一般用2到3个字宽来存储对象头。
-
数组对象,会用3个字宽来存储。
-
非数据对象,则用2个字宽来存储。
其结构简单如下:
从上表中,我们可以看到,锁相关的信息,是存在称之为Markword中的内存域中。
拿以下的代码作为例子,
synchonized(LockObject) { //do something here}
在对象LockObject的对象头中,当其被创建的时候,其Markword的结构如下:
从上面Markword的结构中,可以看出 所有新创建的对象,都是可偏向的(锁标志位为01),但都是未偏向的(是否偏向锁标志位为0)
偏向锁
当线程执行到临界区(critical section)时,此时会利用CAS(Compare and Swap)操作,将线程ID插入到Markword中,同时修改偏向锁的标志位。
这说明此对象就要被当做一个锁来使用,那么其Markword的内容就要发生变化了。 其结构其会变成如下:
可以看到,
-
锁的标志位还是01
-
“是否偏向锁”这个字段变成了1
-
hash值变成了线程ID和epoch值
也就是说,这个锁将自己偏向了当前线程,心里默默地藏着线程id, 在这里,我们就引入了“偏向锁”的概念。
在此线程之后的执行过程中,如果再次进入或者退出同一段同步块代码,并不再需要去进行加锁或者解锁操作,而是会做以下的步骤:
-
Load-and-test,也就是简单判断一下当前线程id是否与Markword当中的线程id是否一致.
-
如果一致,则说明此线程已经成功获得了锁,继续执行下面的代码
-
如果不一致,则要检查一下对象是否还是可偏向,即“是否偏向锁”标志位的值。
-
如果还未偏向,则利用CAS操作来竞争锁,也即是第一次获取锁时的操作。
-
如果此对象已经偏向了,并且不是偏向自己,则说明存在了竞争。此时可能就要根据另外线程的情况,可能是重新偏向,也有可能是做偏向撤销,但大部分情况下就是升级成轻量级锁了。
以下是Java开发人员提供的一张图:

“偏向锁”是Java在1.6引入的一种优化机制,其核心思想在于,可以让同一个线程一直拥有同一个锁,直到出现竞争,才去释放锁。
因为经过虚拟机开发人员的调查研究,在大多数情况下,总是同一个线程去访问同步块代码,基于这样一个假设,引入了偏向锁,只需要用一个CAS操作和简单地判断比较,就可以让一个线程持续地拥有一个锁。 也正因为此假设,在Jdk1.6中,偏向锁的开关是默认开启的,适用于只有一个线程访问同步块的场景。
下篇预告
在上面,我们说到,一旦出现竞争,也即有另外一个线程也要来访问这一段代码,偏向锁就不适用于这种场景了。
如何解决的呢?下篇文章将带大家了解锁膨胀、锁撤销、轻量级锁等内容!持续关注,这样就不会错过之后的精彩内容啦!
如果这篇文章对你有帮助的话,别忘了【点赞】【分享】支持下哦~
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· 【全网最全教程】使用最强DeepSeekR1+联网的火山引擎,没有生成长度限制,DeepSeek本体