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

参数关闭偏向锁。

目录
相关文章
|
6天前
|
存储 Java 调度
深入浅出Java线程池原理
本文深入分析了Java线程池的原理和实现,帮助读者更好地理解Java并发编程中线程池的创建、工作流程和性能优化。
|
5天前
|
安全 Java 数据库
一天十道Java面试题----第四天(线程池复用的原理------>spring事务的实现方式原理以及隔离级别)
这篇文章是关于Java面试题的笔记,涵盖了线程池复用原理、Spring框架基础、AOP和IOC概念、Bean生命周期和作用域、单例Bean的线程安全性、Spring中使用的设计模式、以及Spring事务的实现方式和隔离级别等知识点。
|
2天前
|
存储 SQL 关系型数据库
深入MySQL锁机制:原理、死锁解决及Java防范技巧
深入MySQL锁机制:原理、死锁解决及Java防范技巧
|
9天前
|
Java 调度 开发者
Java并发编程:解锁多线程同步的奥秘
在Java的世界里,并发编程是提升应用性能的关键所在。本文将深入浅出地探讨Java中的并发工具和同步机制,带领读者从基础到进阶,逐步掌握多线程编程的核心技巧。通过实例演示,我们将一起探索如何在多线程环境下保持数据的一致性,以及如何有效利用线程池来管理资源。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你对Java并发编程有更深入的理解和应用。
|
19天前
|
缓存 网络协议 算法
(二)Java网络编程之爆肝HTTP、HTTPS、TLS协议及对称与非对称加密原理!
作为一名程序员,尤其是Java程序员,那必须得了解并掌握HTTP/HTTPS相关知识。因为在如今计算机网络通信中,HTTP协议的作用功不可没,无论是日常上网追剧、冲���、亦或是接口开发、调用等,必然存在HTTP的“影子”在内。尤其对于WEB开发者而言,HTTP几乎是每天会打交道的东西。
45 10
|
15天前
|
存储 安全 Java
解锁Java并发编程奥秘:深入剖析Synchronized关键字的同步机制与实现原理,让多线程安全如磐石般稳固!
【8月更文挑战第4天】Java并发编程中,Synchronized关键字是确保多线程环境下数据一致性与线程安全的基础机制。它可通过修饰实例方法、静态方法或代码块来控制对共享资源的独占访问。Synchronized基于Java对象头中的监视器锁实现,通过MonitorEnter/MonitorExit指令管理锁的获取与释放。示例展示了如何使用Synchronized修饰方法以实现线程间的同步,避免数据竞争。掌握其原理对编写高效安全的多线程程序极为关键。
37 1
|
20天前
|
安全 Java 程序员
Java 并发编程:解锁多线程同步的奥秘
【7月更文挑战第30天】在Java的世界里,并发编程是一块充满挑战的领域。它如同一位严苛的导师,要求我们深入理解其运作机制,才能驾驭多线程的力量。本文将带你探索Java并发编程的核心概念,包括线程同步与通信、锁机制、以及并发集合的使用。我们将通过实例代码,揭示如何在多线程环境中保持数据的一致性和完整性,确保你的应用程序既高效又稳定。准备好了吗?让我们一同踏上这段解锁Java并发之谜的旅程。
26 5
|
20天前
|
存储 SQL Java
(七)全面剖析Java并发编程之线程变量副本ThreadLocal原理分析
在之前的文章:彻底理解Java并发编程之Synchronized关键字实现原理剖析中我们曾初次谈到线程安全问题引发的"三要素":多线程、共享资源/临界资源、非原子性操作,简而言之:在同一时刻,多条线程同时对临界资源进行非原子性操作则有可能产生线程安全问题。
|
19天前
|
网络协议 Java 数据处理
(一)Java网络编程之计网基础、TCP-IP协议簇、TCP、UDP协议及腾讯QQ通信原理综述
就目前而言,多数网络编程的系列的文章都在围绕着计算机网络体系进行阐述,但其中太多理论概念,对于大部分开发者而言,用途甚微。因此,在本系列中则会以实际开发者的工作为核心,从Java程序员的角度出发,详细解读Java的网络编程核心内容。
|
20天前
|
缓存 监控 Java
(十)深入理解Java并发编程之线程池、工作原理、复用原理及源码分析
深入理解Java并发编程之线程池、工作原理、复用原理及源码分析