首先看下面的代码,逻辑很简单,定义了5个线程,这5个线程分别对同一个对象的成员变量num进行10000次递增操作,最后等待所有线程执行完成后,将num结果输出。
正常情况下,我们预期的输出是50000,但运行程序之后,实际输出结果并不是如此,结果可能是50000,也可能小于50000。
为什么会出现结果小于50000的情况呢?
首先,我们要知道,线程在执行"num++"这句代码时,实际上会执行三个动作:
读取num的值;
修改num的值;
将修改后的num刷新到内存中。
由于此处有5个线程同时在执行这段代码,所以可能出现这种情况:线程1读取到了num的值为0,此时,线程1还没有执行修改num值的动作,线程2也读取了num的值,那么线程2读取到的num的值也是0,那此时线程1和线程2对num做递增操作时,都是对0进行加1,所以线程1和线程2的运行结果都是1。可以看到,虽然线程1和线程2都对num执行了一次++操作,但结果并不是2,而是1。
这就是导致我们上面程序的运行结果可能出现不是50000的情况的原因。
通过上面的分析我们知道了出现这种情况的原因是多个线程同时对num这个变量做操作导致的,那我们就知道该如何解决这个问题了:每次只允许一个线程执行该段代码,其他线程要等待这个线程执行完成后再执行该段代码。而synchronized关键字就可以做到这一点。
synchronized是java提供的一种原子性内置锁,Java中的每个对象都可以把它当作一个同步锁来使用,这些Java内置的使用者看不到的锁被称为内部锁,也叫作监视器锁。内置锁是排它锁,也就是当一个线程获取这个锁后, 其他线程必须等待该线程释放锁后才能获取该锁。
synchronized关键字作用在普通方法上,作用范围是整个方法,作用对象为调用这个方法的对象。即同一时间只能有一个线程进入该对象的这个方法,其他线程需在这个方法外等待上个线程退出该方法后才能进入。使用案例:
synchronized关键字作用在某段代码块上,作用范围是处于synchronized同步块中的代码段,作用对象是synchronized括号中的对象。使用案例:
synchronized关键字作用在静态方法上,作用范围是整个静态方法,作用对象是这个类的所有对象。使用案例:
一个对象未被当成锁时,就是一个普通的对象,此时,Mark Word中,是否偏向锁的值是0,锁标志位的值是01;
到这里,我们已经会使用synchronized关键字来解决多线程并发的同步安全问题了,那synchronized是如何实现这种线程同步互斥功能的呢?
从synchronized的3种使用方式中,我们知道,synchronized实际是作用在对象上的,那锁的实现肯定也与对象在内存中的存储有关系。所以,在学习其原理之前,我们有必要先来了解下另一个名词:Mark Word。
JVM虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。对象头又包括两部分信息:运行时数据和类型指针。运行时数据(如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID等等)的长度在32位和64位的虚拟机中分别占32Bits和64Bits(没有开启压缩指针的情况),官方称他为“Mark Word”。在此我向大家推荐一个架构学习交流圈。交流学习指导伪鑫:1253431195(里面有大量的面试题及答案)里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多
在32位虚拟机中,一个对象的Mark Word默认存储结构如下:
此时的对象处于无锁状态,32Bits空间中的25Bits用于存储对象哈希码(HashCode),4Bits用于存储对象分代年龄,2Bits用于存储锁标志位,1Bits固定为0。当给该对象加上不同级别的锁之后(偏向锁、轻量级锁、重量级锁),Mark Word的存储结构如下:
我们可以根据Mark Word中的是否偏向锁(biased_lock)和锁标志位(lock)的值来判断对象当前的状态。
了解了Mark Word之后,我们就可以分析锁的实现原理了。
从Mark Word的存储结构图中,我们可以看到,锁的级别分为:无锁状态、偏向锁、轻量级锁和重量级锁。一个对象从无锁状态->偏向锁->轻量级锁->重量级锁的转化,称为锁的升级。升级过程参考下图:
6.如果上述的更新操作失败了,表示竞争锁失败,此时,JVM会使用自旋锁(相当于一个循环)不断重试尝试获取锁的动作,JDK1.7开始,循环次数由JVM决定。如果自旋尝试获取锁成功,表示当前线程抢占到锁,可以执行同步代码块;
7.如果字段尝试获取锁后依然失败,表示当前锁竞争非常激烈,会被升级为重量级锁,Mark Word中,锁标志位的值被修改为10。此时,其他未抢占到锁的线程都会被阻塞。
以上就是synchronized的实现原理了。过程中涉及一个名词:CAS操作,意思就是拿期望值与旧值相比,如果相等,则更新为新值并返回true,否则不进行更新,返回false。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/MC_XY/article/details/124369525
内容来源于网络,如有侵权,请联系作者删除!