在java中,每个object,也就是每个对象都拥有一把锁,这把锁存放在对象头中,锁中记录了当前对象被那个线程所占有。
对象的结构*
对象头的结构
这里可以参考一篇博客:点我查看
在Java中synchronized被Java编译之后产生的字节码指令monitorenter, monitorexit,依赖这两个指令能够使线程体同步。
使用这两个指令对业务代码进行了包裹
Monitor ——>管程/监视器
你可以将monitor理解成只能容纳一个人的房间,当一个人进入房间的时候,其他人只能等待,只有当这个人出来之后,其他人才能才有进入的机会。
但是存在性能问题,因为synchronized被编译之后实际上是monitorenter和monitorexit两个字节码指令,而monitor依赖的是操作系统的mutex lock来实现的。
java线程实际上是对操作系统线程的映射,所以每当挂起或者唤醒一个线程,都要切换擦作系统的内核态,这种操作时比较重量级的。
所以java6引入了偏向锁和轻量级锁。
针对于锁到底能不能降级,如下:
锁的4种状态
1、无锁
所有线程都能够访问到同一个资源,这就可能出现两种情况:
- 无竞争
- 存在竞争,非锁方式同步线程(CAS)
2、偏向锁
如果这个对象能够认识这个线程,当这个线程来的时候直接会交出锁,偏爱这个对象,就叫偏向锁
当为偏向锁的时候,是根据mark word的前23bit来确定当前获得所得线程,如果下次还是还是这个id,那就直接给,但是如果下次的不一样,就代表有多个线程在争抢锁,那就升级为轻量级锁。
3、轻量级锁
将前30bit转换为指向栈中所记录的指针。
当一个线程想要获得某个对象的锁时,假如看到锁标记位为00,那么就知道他是轻量级锁,这是线程会在自己的虚拟机栈中开辟一块空间称为Lock Record。lock record 中存放的是对象头中mark word的副本和指向owner指针,线程通过CAS去尝试获取锁,一旦获得那么将会复制该对象头中的mark word副本到lock record中,并将owner指针指向该对象, 并且对象mark word的前30位指针会指向线程虚拟机栈中的lock record,这样就实现了线程和对象锁的绑定,他们就互相知道了对方的存在。
如果现在处于轻量级锁并且有一个线程正在占用锁,另外一个线程想要获取锁,那么这个线程就会自旋,等待锁释放。
如果自旋等待的锁的线程超过一个,那么就会升级重量级锁。