重量级锁

JDK1.5之前默认的锁的形态。
若自旋失败(依然无法获取到锁),那么锁就会转化为重量级锁,在这种情况下,无法获取到锁的线程都会进入到Monitor(即内核态)。
重量级锁通过对象内部的监视器(Monitor)实现,其中 Monitor 的本质是依赖于底层操作系统的 Mutex Lock 实现,操作系统实现线程之间的切换需要从用户态到内核态的切换,切换成本非常高。

源码

重量级锁 - 图1

  • Synchornized重量级锁 中park()调用的是 pthread_mutex_init() 方法
  • ReentrantLock 中调用的LockSupport.park()所调用的 pthread_cond_init() 方法
  • 两者都会进入内核态,区别不大。

https://blog.csdn.net/weixin_39687783/article/details/85058686

线程等待与唤醒

Monitor

JVM中的同步是基于进入与退出监视器对象(管程对象)(Monitor)来实现的,每个对象实例都会有一个Monitor对象,Monitor对象会和Java对象一同创建并销毁。Monitor对象是由C++来实现的。

image.png


  1. 当多个线程同时访问一段同步代码时,这些线程会被放到一个EntryList集合中,处于阻塞状态的线程都会被放到该列表当中。接下来,当线程获取到对象的Monitor时,Monitor是依赖于底层操作系统的mutex lock(互斥锁)来实现互斥的,线程获取mutex成功,则会持有该mutex,这时其他线程就无法再获取到该mutex。<br /> 如果线程调用了wait方法,那么该线程就会释放掉所持有的mutex,并且该线程会进入到WaitSet集合(等待集合)中,等待下一次被其他线程调用notify/notifyAll唤醒。如果当前线程顺利执行完毕方法,那么它也会释放掉所持有的mutex。<br /> 通过对象互斥锁的概念来保证共享数据操作的完整性。每个对象都对应于一个可称为『互斥锁』的标记,这个标记用于保证在任何时刻,只能有一个线程访问该对象。

:::success ** :::


:::info
那些处于EntryList与WaitSet中的线程均处于阻塞状态,阻塞操作是由操作系统来完成的,在linux下是通过pthread_mutex_lock函数实现的。
线程被阻塞后便会进入到内核调度状态,当他们获取到了锁后又会回到用户态**,这会导致系统在用户态与内核态之间来回切换,严重影响锁的性能。 :::