在多线程并发编程中synchronized的一直是元老级别角色,通过对象的内置锁实现互斥访问共同变量,通常被称为重量级锁,但是为了减少获得锁和释放锁带来的性能消耗,在jdk 1.6中引入了偏向锁和轻量级锁。在以下博文中,我们先讨论其实现底层原理以及他的锁升级过程。
java 中每一个对象可作为锁,具体有三种表现形式:
1)对于普通同步方法,锁是当前实例对象
2)对于静态同步方法,锁是当前类的class 对象
3)对于同步方法快,锁是Synchronized 括号里面的对象。
JVM基于进入和退出Monitor对象来实现方法和代码块同步,monitorenter指令在编译后插入到同步代码块开始位子,而monitoreixt则插入到方法结束或异常处。当一个monitor被持有后,则处于锁定状态。
synchronized 用的锁存在java 对象头( 拓展一、对象在内存中布局分为三个部分:①对象头:一部分存放对象自身运行时候的数据,称为mark word,一部分为类型指针,确定对象所属类;②实例数据部分:对象真正存储的有效信息,无论是父类继承下来还是子类中定义的;③对齐填充,起着占位符作用。拓展二、Class 文件中除了有类的版本,字段,方法,接口等描述信息,还有一项是常量池,存放编译器的字面量和符号引用(在加载后进入运行常量池)通过类的符号引用可以判断类是否被加载过)里面,而对象头里面的Mark Word存放锁状态(无锁,偏向锁,轻量级硕,重量级锁),对象的hashcode,分代年龄,锁标记位。JVM在锁的获得和释放中将改变对象锁的状态。
1)偏向锁:在锁不仅不存在多个线程竞争,而且总是由一个进程多次获得,为了使获得锁代价耕地而引入偏向锁,获取锁时候,会在对象头和栈帧中的锁记录(每一个线程有独立的Jvm虚拟机栈,锁记录是将对象头中的Mark Word复制到其中)中存储锁偏向的线程ID,以后线程在进入和推出同步快时候不需要CAS操作来加锁和解锁,只要简单测试下当前线程的ID是否为对象头重存储的线程ID。偏向锁使用等待竞争出现才释放锁的机制,等待全局安全点,暂停拥有偏向锁的进程,要么恢复到无所状态,或则升级为轻量级锁。
2)轻量级锁:加锁时,先对锁记录进行对象锁复制(锁标志位01,未锁定);线程使用CAS(Compare and Swap)算法修改对象头MarkWord指向锁记录,修改成功则加锁成功(对象所标志位00,锁定)。失败则表示有其他线程竞争锁(通过标志位状态是否是00判断是否处于轻量级锁状态锁定),则自旋去获得锁。释放锁时候,同样使用原子的CAS操作修改MarkWord替换到对象头。如果成功,则表示同步过程完成;否则,表明有其他进程尝试获取锁(对象锁已经升级到重量级锁,标志位为10)则需要释放锁唤醒等待的线程。
2018-03-24