上回我们说到了synchronized和lock的区别,相信你们对synchronized关键字已经有了一定的了解,这次我们就来聊聊synchronized有哪些底层实现原理。
一、运行机制
Java中,JVM的同步一定是基于进入和退出Monitor对象实现的,不管你是显式同步或者隐式同步都是一样的。同步在java中用的最多的地方就是被synchronized修饰的同步方法。同步方法,它并不是由monitorenter和monitorexit 指令来实现,它会由方法调用指令读取运行时常量池上方法表结构的ACC_SYNCHRONIZED 标志来隐式实现。
二、数据填充
JVM虚拟机要求一个对象它的起始地址必须是8字节的整数倍。但实际来说,填充数据是不需要必须存在的,它仅仅是为了让字节对齐而已。
三、实例变量
这种变量一般会存放类的属性数据信息,包括父类的属性信息,但如果是数组的实例部分且包括数组长度,那么这一部分内存就会按4字节对齐。
四、同步代码块
JVM会使用monitorenter指令来将代码插入到同步代码块的开始位置,用monitorexit指令插入到同步代码块的结束位置。同时,JVM需要保证每一个monitorenter都有一个monitorexit与之相对应。在代码块中,它的任何对象都必须有一个monitor与之相关联,并且当一个monitor被持有之后,它会处于锁定状态。一般当线程执行到monitorenter这个指令时,都会尝试着获取对象所对应的monitor所有权,这个过程就叫尝试获取对象的锁。
五、对象头
Java中,Hotspot虚拟机的对象头主要包括两部分数据:
1.Mark Word(标记字段)
2.Klass Pointer(类型指针)。
其中Klass Point是指对象指向它的类元数据的指针,虚拟机能够通过这个指针来确定这个对象是哪个类的实例;Mark Word通常适用于存储对象自身的运行时数据,实现轻量级及偏向锁的关键就是它。
六、Mark Word
这个家伙用于存储对象自身的运行时数据,如线程持有的锁、偏向时间戳、偏向线程 ID等等。Java对象头一般会占有两个机器码,都在32位虚拟机中,1个机器码等于4字节,然如若它的对象是数组类型,就会需要三个机器码,这是因为JVM虚拟机可以通过Java对象的元数据信息来确定Java对象的大小,但它无法从数组的元数据来确认数组的大小,所以会用一块来记录数组长度。
七、Monior
Monior这个家伙有很多说法,有人说它是同步工具,也有人说它是同步机制,但不可否认的是,它是为同步而生的,它在java中一般会被描述为一个对象。我们都知道,java是面向对象思想的,它认为世间一切皆对象,这就使得每一个java对象就是一个个天生的Monitor,每一个实例的java对象都有着成为monitor的潜力,这是因为在Java设计中 ,每一个Java对象从初始化时就带了一把看不见的锁,这个锁就是内部锁或者也可以叫它Monitor锁。Monitor 是线程私有的数据结构,每一个线程都会有一个可用monitor record列表,同时还有一个全局的可用列表。每一个被锁住的对象都会与一个monitor关联,同时,在monitor中会有一个Owner字段来存放拥有该锁的线程的唯一标识,表示着锁被这个线程占用。它的结构如下:
EntryQ:关联一个系统互斥锁(semaphore),阻塞所有试图锁住monitor record失败的线程。
RcThis:表示blocked或waiting在该monitor record上的所有线程的个数。
Nest:用来实现重入锁的计数。
HashCode:保存从对象头拷贝过来的HashCode值(可能还包含GC age)。
Candidate:用来避免不必要的阻塞或等待线程唤醒,因为每一次只有一个线程能够成功拥有锁,如果每次前一个释放锁的线程唤醒所有正在阻塞或等待的线程,会引起不必要的上下文切换(从阻塞到就绪然后因为竞争锁失败又被阻塞)从而导致性能严重下降。Candidate只有两种可能的值0表示没有需要唤醒的线程1表示要唤醒一个继任线程来竞争锁。
以上就是今天的全部内容了,java中每个关键字都有着不可撼动的地位,如若还想了解更多java架构师相关内容,就请持续关注奇Q工具网吧。
推荐阅读: