1 字节码层实现
javap 生成的字节码中包含如下指令:
- monitorenter
- monitorexit
synchronized基此实现了简单直接的锁的获取和释放。
当JVM的解释器执行monitorenter时会进入到
InterpreterRuntime.cpp
的
1.1 InterpreterRuntime::monitorenter
// 解释器的同步代码被分解,以便方法调用和同步块共享。 JRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem)) #ifdef ASSERT thread->last_frame().interpreter_frame_verify_monitor(elem); #endif if (PrintBiasedLockingStatistics) { Atomic::inc(BiasedLocking::slow_path_entry_count_addr()); } Handle h_obj(thread, elem->obj()); assert(Universe::heap()->is_in_reserved_or_null(h_obj()), "must be NULL or an object"); if (UseBiasedLocking) { // 如果取消了偏向,请重试快速进入,以避免不必要的🔐膨胀 ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK); } else { ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK); } assert(Universe::heap()->is_in_reserved_or_null(elem->obj()), "must be NULL or an object"); #ifdef ASSERT thread->last_frame().interpreter_frame_verify_monitor(elem); #endif JRT_END
1.1.1 函数参数
- JavaThread *thread
封装 Java线程 帧状态的与机器/操作系统相关的部分的对象,这里传参代表程序中的当前线程 - BasicObjectLock *elem
BasicLock 类型的 _lock 对象主要用来保存 _obj 对象的对象头数据:
1.1.2 函数体
UseBiasedLocking 标识JVM是否开启偏向锁功能
- 如果开启则执行fast_enter逻辑
- 否则执行slow_enter
2 偏向锁
2.1 偏向锁的意义
无多线程竞争时,尽量减少不必要的轻量级锁执行路径。
轻量级锁的获取及释放依赖多次的CAS操作,而偏向锁只依赖一次CAS置换ThreadID。
当存在高度的锁竞争和低数据竞争时,RTM 锁最有用。
高锁争用情况下,锁通常会膨胀,而偏向锁不适于这种情况。
RTM 锁代码要求关闭偏向锁。
注意:我们不能在 get_processor_features() 中关闭 UseBiasedLocking,因为它被 Thread::allocate() 使用,它在 VM_Version::initialize() 之前调用。
if (UseRTMLocking && UseBiasedLocking) { if (FLAG_IS_DEFAULT(UseBiasedLocking)) { FLAG_SET_DEFAULT(UseBiasedLocking, false); } else { warning("Biased locking is not supported with RTM locking; ignoring UseBiasedLocking flag." ); UseBiasedLocking = false; } }
一旦出现多个线程竞争时必须撤销偏向锁,所以:
撤销偏向锁消耗的性能必须 < 之前节省下来的CAS原子操作的性能消耗
不然得不偿失!
JDK 6中默认开启偏向锁,可以通过-XX:-UseBiasedLocking
禁用偏向锁。
- 偏向锁的入口位于
synchronizer.cpp
文件的ObjectSynchronizer::fast_enter
函数
2.2 偏向锁的获取
由BiasedLocking::revoke_and_rebias
方法实现
2.2.1 markOop mark = obj->mark()
获取对象的markOop数据mark,即对象头的Mark Word
2.2.2 判断mark是否为可偏向状态
- mark的偏向锁标志位为 1 锁标志位为 01
2.2.3 判断mark中JavaThread的状态
如果为空,则进入步骤(4);如果指向当前线程,则执行同步代码块;如果指向其它线程,进入步骤(5);
2.2.4 通过CAS原子指令
设置mark中JavaThread为当前线程ID,如果执行CAS成功,则执行同步代码块,否则进入步骤(5);
2.2.5 如果执行CAS失败
表示当前存在多个线程竞争锁,当达到全局安全点(safepoint),获得偏向锁的线程被挂起,撤销偏向锁,并升级为轻量级,升级完成后被阻塞在安全点的线程继续执行同步代码块;
2.3 偏向锁的撤销
只有当其它线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,偏向锁的撤销由BiasedLocking::revoke_at_safepoint
方法实现:
1、偏向锁的撤销动作必须等待全局安全点;
2、暂停拥有偏向锁的线程,判断锁对象是否处于被锁定状态;
3、撤销偏向锁,恢复到无锁(标志位为 01)或轻量级锁(标志位为 00)的状态;
偏向锁在Java 1.6之后是默认启用的,但在应用程序启动几秒钟之后才激活,可以使用
-XX:BiasedLockingStartupDelay=0
参数关闭延迟,如果确定应用程序中所有锁通常情况下处于竞争状态,可以通过
XX:-UseBiasedLocking=false
参数关闭偏向锁。