Java进阶笔记——synchronized 关键字原理
众所周知 synchronized
关键字是解决并发问题常用解决方案,有以下三种使用方式:
- 同步普通方法,锁的是当前对象。
- 同步静态方法,锁的是当前
Class
对象。 - 同步块,锁的是
()
中的对象。
实现原理:JVM
是通过进入、退出对象监视器( Monitor
)来实现对方法、同步块的同步的。
具体实现是在编译之后在同步方法调用前加入一个 monitor.enter
指令,在退出方法和异常处插入 monitor.exit
的指令。
其本质就是对一个对象监视器( Monitor
)进行获取,而这个获取过程具有排他性从而达到了同一时刻只能一个线程访问的目的。
而对于没有获取到锁的线程将会阻塞到方法入口处,直到获取锁的线程 monitor.exit
之后才能尝试继续获取锁。
流程图如下:
通过一段代码来演示:
1 2 3 4 5 |
public static void main(String[] args) { synchronized (Synchronize.class){ System.out.println("Synchronize"); } } |
使用 javap -c Synchronize
可以查看编译之后的具体信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public class com.crossoverjie.synchronize.Synchronize { public com.crossoverjie.synchronize.Synchronize(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: ldc #2 // class com/crossoverjie/synchronize/Synchronize 2: dup 3: astore_1 **4: monitorenter** 5: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 8: ldc #4 // String Synchronize 10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 13: aload_1 **14: monitorexit** 15: goto 23 18: astore_2 19: aload_1 20: monitorexit 21: aload_2 22: athrow 23: return Exception table: from to target type 5 15 18 any 18 21 18 any } |
可以看到在同步块的入口和出口分别有 monitorenter,monitorexit
指令。
锁优化
synchronized
很多都称之为重量锁,JDK1.6
中对 synchronized
进行了各种优化,为了能减少获取和释放锁带来的消耗引入了偏向锁
和轻量锁
。
轻量锁
当代码进入同步块时,如果同步对象为无锁状态时,当前线程会在栈帧中创建一个锁记录(Lock Record
)区域,同时将锁对象的对象头中 Mark Word
拷贝到锁记录中,再尝试使用 CAS
将 Mark Word
更新为指向锁记录的指针。
如果更新成功,当前线程就获得了锁。
如果更新失败 JVM
会先检查锁对象的 Mark Word
是否指向当前线程的锁记录。
如果是则说明当前线程拥有锁对象的锁,可以直接进入同步块。
不是则说明有其他线程抢占了锁,如果存在多个线程同时竞争一把锁,轻量锁就会膨胀为重量锁。
解锁
轻量锁的解锁过程也是利用 CAS
来实现的,会尝试锁记录替换回锁对象的 Mark Word
。如果替换成功则说明整个同步操作完成,失败则说明有其他线程尝试获取锁,这时就会唤醒被挂起的线程(此时已经膨胀为重量锁
)
轻量锁能提升性能的原因:
认为大多数锁在整个同步周期都不存在竞争,所以使用 CAS
比使用互斥开销更少。但如果锁竞争激烈,轻量锁就不但有互斥的开销,还有 CAS
的开销,甚至比重量锁更慢。
偏向锁
为了进一步的降低获取锁的代价,JDK1.6
之后还引入了偏向锁。
偏向锁的特征是:锁不存在多线程竞争,并且应由一个线程多次获得锁。
当线程访问同步块时,会使用 CAS
将线程 ID 更新到锁对象的 Mark Word
中,如果更新成功则获得偏向锁,并且之后每次进入这个对象锁相关的同步块时都不需要再次获取锁了。
释放锁
当有另外一个线程获取这个锁时,持有偏向锁的线程就会释放锁,释放时会等待全局安全点(这一时刻没有字节码运行),接着会暂停拥有偏向锁的线程,根据锁对象目前是否被锁来判定将对象头中的 Mark Word
设置为无锁或者是轻量锁状态。
偏向锁可以提高带有同步却没有竞争的程序性能,但如果程序中大多数锁都存在竞争时,那偏向锁就起不到太大作用。可以使用 -XX:-userBiasedLocking=false
来关闭偏向锁,并默认进入轻量锁。
其他优化
适应性自旋
在使用 CAS
时,如果操作失败,CAS
会自旋再次尝试。由于自旋是需要消耗 CPU
资源的,所以如果长期自旋就白白浪费了 CPU
。JDK1.6
加入了适应性自旋:
如果某个锁自旋很少成功获得,那么下一次就会减少自旋。
总结
synchronized 现在已经不像以前那么重了,拿 1.8 中的 ConcurrentHashMap 就可以看出,里面大量的使用了 synchronized 来进行同步。
1、具有1-5工作经验的,面对目前流行的技术不知从何下手,需要突破技术瓶颈的可以加群。
2、在公司待久了,过得很安逸,但跳槽时面试碰壁。需要在短时间内进修、跳槽拿高薪的可以加群。
3、如果没有工作经验,但基础非常扎实,对java工作机制,常用设计思想,常用java开发框架掌握熟练的,可以加群。
4、觉得自己很牛B,一般需求都能搞定。但是所学的知识点没有系统化,很难在技术领域继续突破的可以加群。
5.群号 468947140
6.阿里Java高级大牛直播讲解知识点,分享知识,知识点都是各位老师多年工作经验的梳理和总结,带着大家全面、科学地建立自己的技术体系和技术认知!

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
-
上一篇
JAVA中字符串比较equals()和equalsIgnoreCase()的区别
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xmt1139057136/article/details/81869532 1、使用equals( )方法比较两个字符串是否相等。它具有如下的一般形式: boolean equals(Object str) 这里str是一个用来与调用字符串(String)对象做比较的字符串(String)对象。如果两个字符串具有相同的字符和长度,它返回true,否则返回false。这种比较是区分大小写的。 2、为了执行忽略大小写的比较,可以调用equalsIgnoreCase( )方法。当比较两个字符串时,它会认为A-Z和a-z是一样的。其一般形式如下: boolean equalsIgnoreCase(String str) 这里,str是一个用来与调用字符串(String)对象做比较的字符串(String)对象。如果两个字符串具有相同的字符和长度,它也返回true,否则返回false。下面的例子说明了equals( )和equalsIgnoreCase( )方法: // Demonstrate eq...
-
下一篇
PSR规范0-4整理
PSR规范 psr规范 引言: PSR 是 PHP Standard Recommendations 的简写,由 PHP FIG 组织制定的 PHP 规范,是 PHP 开发的实践标准。这些规范的目的是:通过框架作者或者框架的代表之间讨论,以最低程度的限制,制定一个协作标准,各个框架遵循统一的编码规范,避免各家自行发展的风格阻碍了 PHP 的发展,解决这个程序设计师由来已久的困扰。截止到笔者文章psr在用的共11套规范,下面介绍了其中四个和已经弃用的psr0规范。 PSR-0 (Autoloading Standard) 自动加载标准 (2014年10月21起被官方弃用 由psr4替代) PSR-1 (Basic Coding Standard) 基础编码标准 PSR-2 (Coding Style Guide) 编码风格向导 PSR-3 (Logger Interface) 日志接口 PSR-4 (Improved Autoloading) 自动加载的在用版本。 #注:另有psr6(缓存接口规范)psr7(HTTP消息接口规范)psr11、psr13、psr15、psr16、psr17...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- MySQL数据库在高并发下的优化方案
- Hadoop3单机部署,实现最简伪集群
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Windows10,CentOS7,CentOS8安装Nodejs环境
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- SpringBoot2配置默认Tomcat设置,开启更多高级功能