目录
一、Synchronized锁表现的三种形式
- 对于普通方法,锁的是当前的是对象
- 对于静态同步方法,锁的是当前类的class对象
- 对于同步代码块,锁的是synchronized括号里配置的对象
二、Synchronized的底层实现
1. 为什么synchronized被叫做重量级锁
synchronized是通过对内部的监视器锁(monitor)来实现的,而监视器锁又是依赖于操作系统中的互斥锁(Mutex Lock)来实现的,而操作系统实现线程之间的切换从用户态转换为内核态,这个成本会非常的高,转换需要的时间需要相对比较长的时间,这也是为什么synchronized效率低的原因,所有底层依赖于Mutex Lock 实现的锁,我们称之为重量级锁
synchronized的底层实现是完全依赖JVM虚拟机的,所以谈synchronized的底层实现,就不得不谈数据在JVM内存的存储:Java对象头,以及Monitor对象监视器。
2.对象在内存存储中的存储布局
-
类型指针(class Pointer)
是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。 -
标记字段(Mark Word)
用于存储对象自身的运行时数据,如哈希码(HashCode)、GC信息、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等等,它是实现轻量级锁和偏向锁的关键。 -
实例数据(instance data):记录了对象里面的变量数据。
-
对齐填充 (padding):作为对齐使用,对象在64位服务版本中,规定对象内存必须要能被8字节整除,如果不能整除,那么久靠对齐来不。举个例子:new出了一个对象,内存只占用18字节,但是规定要能被8整除,所以padding=6
-
Monitor
monitor描述为对象监视器,可以类比为一个特殊的房间,这个房间中有一些被保护的数据,monitor保证每次只能有一个线程能进入这个房间进行访问被保护的数据,进入房间即为持有monitor,退出房间即为释放monitor。
3. synchronized 的锁升级
锁解决了数据的安全性,但是同样带来了性能的下降,hotspot 虚拟机的作者经过调查发现,大部分情况下,加锁的代码不仅仅不存在多线程竞争,而且总是由同一个线程多次获得。
所以基于这样一个概率,synchronized 在JDK1.6 之后做了一些优化,为了减少获得锁和释放锁来的性能开销,引入了偏向锁、轻量级锁,锁的状态根据竞争激烈的程度从低到高不断升级。
1.无锁
无锁没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功。
2.偏向锁
偏向锁是JDK6中引入的一项锁优化,大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。
偏向锁会偏向于第一个获得它的线程,如果在接下来的执行过程中,该锁没有被其他的线程获取,则持有偏向锁的线程将永远不需要同步。
3.轻量级锁
是指当锁是偏向锁的时候,被另外的线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,从而提高性能。
4.重量级锁
指的是原始的Synchronized的实现,重量级锁的特点:其他线程试图获取锁时,都会被阻塞,只有持有锁的线程释放锁之后才会唤醒这些线程。
简单描述:
- 没有线程访问时,处于无锁状态
- 当有一个线程访问时,则转为偏向锁
- 当有第二个锁来访问时,转为轻量锁
- 轻量级锁以自旋的方式获取锁,当自旋的次数达到阈值时(默认为10次),则升级为重量级锁