菜鸡每日一面系列打卡15天
每天一道面试题目
助力小伙伴轻松拿offer
坚持就是胜利,我们一起努力!
题目描述
谈谈你对synchronized关键字的理解。
题目分析
相比之前总结的有关volatile关键字的考查,面试官对synchronized关键字的考查更是有过之而无不及,而且对synchronized与volatile关键字往往是结合在一起考查的。上一篇文章系统介绍了有关volatile关键字的作用、原理及使用场景,对volatile关键字尚不清楚的小伙伴,可以移步文末相关链接进行学习,或者点击文章开始的每日一面专辑进行查看,而本文将系统介绍synchronized关键字。
题目解答
01
线程安全的定义
在谈synchronized关键字之前,菜鸡想先谈一谈线程安全的概念。相信绝大多数小伙伴都听说过“线程安全”这一概念,想要了解一个概念,最好的方式就是从其定义出发。
线程安全的定义(引自《Java并发编程实战》)如下:当多个线程同时访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那就称这个对象是线程安全的。
根据以上描述,结合大佬们的经验,我们可以总结出非线程安全的条件:
多线程环境。
共享资源。
没有保证原子性、可见性和有序性。
容易对这三个条件做出说明。首先,如果是单线程的环境下,我们自然不必关系非线程安全的问题。其次,如果是没有线程间共享的资源,也就不会出现线程安全的问题。最后,如果保证了操作的原子性、可见性和有序性,也就不会有线程安全的问题。事实上,Java内存模型就是围绕着在并发过程中如何处理原子性、可见性和有序性这三个特征来建立的。
02
实现线程安全的方式
有经验的小伙伴应该了解,实现线程安全的方式主要有三种:
互斥同步:例如,基于synchronized关键字或者Lock接口实现同步。
非阻塞同步:例如,基于CAS操作的原子类,但CAS操作会出现ABA问题,如果需要解决ABA问题,可以引入版本号,也可以改用其他同步方案。
无同步方案:例如,通过ThreadLocal实现线程本地存储。
本文关注的重点是JVM对原子性、可见性、有序性所作的支持:
关于原子性,保证原子性的方式有很多种,除了操作本身是原子性之外,还可以采用一些原子类保证原子性,可以通过加锁的方式,或者无锁算法保证原子性。本文主要讲述的是利用synchronized关键字保证原子性。
关于可见性,Java主要有三个关键字可以保证可见性,一个是我们之前讲过的volatile关键字,另一个是final关键字,还有一个就是本文提到的synchronized关键字。
关于有序性,Java主要有两个关键字可以保证有序性,一个是我们之前讲过的volatile关键字,另一个就是本文提到的synchronized关键字。
03
synchronized关键字
至此,本文的主角synchronized关键字终于登场了!synchronized关键字是Java中实现互斥同步最基本的手段,它是一种悲观锁,是一种可重入锁(通过锁计数器是否为0判定对象持有或者释放锁的状态)。
synchronized关键字的工作原理:
在同步语句块时,synchronized关键字经过编译之后,会在同步块的前后形成monitorenter和monitorexit两个字节码指令,代表对象实例或者Class对象作为线程要持有的锁。
在修饰方法时,synchronized关键字经过编译之后,会生成ACC_SYNCHRONIZED标识,代表该方法是一个同步方法。
synchronized关键字的不足及改进:
synchronized是Java语言的一个重量级操作,因为线程的阻塞与唤醒会涉及到操作系统用户态与核心态之间的转换,会消耗大量的时间。
从JDK1.5升级到JDK1.6后,HotSpot虚拟机开发团队实现了各种锁优化技术,例如,适应性自旋锁,锁消除,锁膨胀,轻量级锁,偏向锁等优化,从而实现线程间更高效的数据共享和解决竞争问题。
04
总结
synchronized关键字不仅仅是一个用于保证线程安全的关键字,它反映的是Java开发团队乃至计算机科学界对线程安全的一些共识,如何保证线程安全是一门很深的学问,可以深究到操作系统的细节。如何高效实现线程安全是一门艺术,因此,掌握synchronized关键字背后的设计思想,将对于我们进行并发编程有很大的帮助。
以上便是菜鸡对synchronized关键字的一些总结,供大家参考。
学习 | 工作 | 分享
????长按关注“有理想的菜鸡”
只有你想不到,没有你学不到