Java同步关键字synchronize底层实现原理(上)

简介: javap 生成的字节码中包含如下指令:monitorentermonitorexitsynchronized基此实现了简单直接的锁的获取和释放。当JVM的解释器执行monitorenter时会进入到InterpreterRuntime.cpp的

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

image.png

BasicLock 类型的 _lock 对象主要用来保存 _obj 对象的对象头数据:

image.png

1.1.2 函数体

image.png

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函数

image.png

2.2 偏向锁的获取

BiasedLocking::revoke_and_rebias方法实现

image.png

2.2.1 markOop mark = obj->mark()

获取对象的markOop数据mark,即对象头的Mark Word

image.png

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方法实现:

image.png

1、偏向锁的撤销动作必须等待全局安全点;

2、暂停拥有偏向锁的线程,判断锁对象是否处于被锁定状态;

3、撤销偏向锁,恢复到无锁(标志位为 01)或轻量级锁(标志位为 00)的状态;

偏向锁在Java 1.6之后是默认启用的,但在应用程序启动几秒钟之后才激活,可以使用

-XX:BiasedLockingStartupDelay=0

参数关闭延迟,如果确定应用程序中所有锁通常情况下处于竞争状态,可以通过

XX:-UseBiasedLocking=false

参数关闭偏向锁。

目录
相关文章
|
5月前
|
存储 缓存 Java
我们来详细讲一讲 Java NIO 底层原理
我是小假 期待与你的下一次相遇 ~
205 2
|
4月前
|
监控 Java API
现代 Java IO 高性能实践从原理到落地的高效实现路径与实战指南
本文深入解析现代Java高性能IO实践,涵盖异步非阻塞IO、操作系统优化、大文件处理、响应式网络编程与数据库访问,结合Netty、Reactor等技术落地高并发应用,助力构建高效可扩展的IO系统。
148 0
|
4月前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
200 0
|
6月前
|
存储 缓存 Java
【高薪程序员必看】万字长文拆解Java并发编程!(5):深入理解JMM:Java内存模型的三大特性与volatile底层原理
JMM,Java Memory Model,Java内存模型,定义了主内存,工作内存,确保Java在不同平台上的正确运行主内存Main Memory:所有线程共享的内存区域,所有的变量都存储在主存中工作内存Working Memory:每个线程拥有自己的工作内存,用于保存变量的副本.线程执行过程中先将主内存中的变量读到工作内存中,对变量进行操作之后再将变量写入主内存,jvm概念说明主内存所有线程共享的内存区域,存储原始变量(堆内存中的对象实例和静态变量)工作内存。
232 0
|
5月前
|
存储 算法 安全
Java中的对称加密算法的原理与实现
本文详细解析了Java中三种常用对称加密算法(AES、DES、3DES)的实现原理及应用。对称加密使用相同密钥进行加解密,适合数据安全传输与存储。AES作为现代标准,支持128/192/256位密钥,安全性高;DES采用56位密钥,现已不够安全;3DES通过三重加密增强安全性,但性能较低。文章提供了各算法的具体Java代码示例,便于快速上手实现加密解密操作,帮助用户根据需求选择合适的加密方案保护数据安全。
415 58
|
4月前
|
人工智能 安全 Java
Go与Java泛型原理简介
本文介绍了Go与Java泛型的实现原理。Go通过单态化为不同类型生成函数副本,提升运行效率;而Java则采用类型擦除,将泛型转为Object类型处理,保持兼容性但牺牲部分类型安全。两种机制各有优劣,适用于不同场景。
166 24
|
5月前
|
XML JSON Java
Java 反射:从原理到实战的全面解析与应用指南
本文深度解析Java反射机制,从原理到实战应用全覆盖。首先讲解反射的概念与核心原理,包括类加载过程和`Class`对象的作用;接着详细分析反射的核心API用法,如`Class`、`Constructor`、`Method`和`Field`的操作方法;最后通过动态代理和注解驱动配置解析等实战场景,帮助读者掌握反射技术的实际应用。内容翔实,适合希望深入理解Java反射机制的开发者。
548 13
|
4月前
|
存储 缓存 安全
深入讲解 Java 并发编程核心原理与应用案例
本教程全面讲解Java并发编程,涵盖并发基础、线程安全、同步机制、并发工具类、线程池及实际应用案例,助你掌握多线程开发核心技术,提升程序性能与响应能力。
218 0
|
5月前
|
算法 Java 索引
说一说 Java 并发队列原理剖析
我是小假 期待与你的下一次相遇 ~