synchronized 的 monitor 机制

news/2024/5/17 15:21:44 标签: java, synchronized, 并发编程, jvm

synchronized__monitor__0">synchronized 的 monitor 机制

前言

  1. 本文基于 jdk 8 编写。
  2. @author JellyfishMIX - github / blog.jellyfishmix.com
  3. LICENSE GPL-2.0

monitor

  1. monitor 是 synchronized 中用以实现线程之间的互斥与协作的主要手段,它可以看成是对象或者 class 持有的锁。每一个对象有且仅有一个 monitor。

  2. 这也是为什么会看到 synchronized(object) 或 synchronized(MainActivity.class) 这样的写法,这样指明了使用了什么对象或类持有的 monitor。

  3. 下图描述了线程和 monitor之间关系,以及线程的状态转换图。

    img

  4. 如上图,每个 monitor 在每个时刻,只能被一个线程持有,该线程就是 activeThread,其它线程都是 waitingThread,分别在两个队列 entrySet 和 waitSet 里等候。在 entrySet 中等待的线程状态是 waiting for monitor entry,而在 wait set 中等待的线程状态是 in Object.wait()。

entrySet

  1. 先看 entrySet 里面的线程。我们称被 synchronized 包起来的代码段为临界区。

    java">synchronized(object) {
       // ......
    }
    
  2. 当一个线程尝试进入临界区时,有两种可能性:

    1. 该 monitor 不被其它线程持有,entrySet 里面也没有其它等待线程。尝试获取 monitor 的当前线程即成为对应对象或类的 monitor 的 owner,执行临界区的代码。

    2. 该 monitor 被其它线程拥有,尝试获取 monitor 的当前线程在 entrySet 队列中等待并休眠。

    3. 第一种情况下,线程将处于 Runnable 的状态。第二种情况下,线程 dump 输出信息会显示处于 waiting for monitor entry。如下:

      "Thread-0" prio=10 tid=0x08222eb0 nid=0x9 waiting for monitor entry [0xf927b000..0xf927bdb8] 
      at testthread.WaitThread.run(WaitThread.java:39) 
      - waiting to lock <0xef63bf08> (a java.lang.Object) 
      - locked <0xef63beb8> (a java.util.ArrayList) 
      at java.lang.Thread.run(Thread.java:595) 
      
  3. 临界区的机制,是为了保证其内部代码执行的原子性,消除竞态条件。

  4. 需要注意的是,临界区在任何时间只允许线程串行通过,这和我们使用多线程提高性能的初衷是相反的。如果在多线程的程序中,大量使用 synchronized,或者不恰当地使用了它,会造成大量线程在临界区的入口等待获取 monitor,造成性能大幅下降。通过 dump 排查问题时,这也是要排查的点。

  5. 排查线程阻塞的问题时,cpu 忙则关注 Runnable 的线程,可能正在运行的线程数量太多了,比如线程池 coreSize 设置的太大了。cpu 闲则关注 dump 输出信息中的 waiting for monitor entry,可能大量线程阻塞在获取 monitor。只有获得了 monitor 的线程在占用 cpu 时间片运行,cpu 利用率低。

waitSet

  1. 当线程获得了 monitor,进入临界区之后,如果代码编写调用了 monitor 对应的对象(一般是被 synchronized 修饰的对象)的 wait() 方法,此时放弃 monitor,进入 waitSet 队列并休眠。

  2. 只有别的线程在该对象上调用了 notify() 或 notifyAll() 方法,waitSet 队列中线程才得到机会去竞争。但是只有一个线程能获得此对象的 monitor,线程状态恢复到 Runnable,其他线程继续在 waitSet 中等待并休眠。

  3. 在 waitSet 中的线程,dump 输出信息中表现为: in Object.wait()。

    java">"Thread-1" prio=10 tid=0x08223250 nid=0xa in Object.wait() [0xef47a000..0xef47aa38] 
     at java.lang.Object.wait(Native Method) 
     - waiting on <0xef63beb8> (a java.util.ArrayList) 
     at java.lang.Object.wait(Object.java:474) 
     at testthread.MyWaitThread.run(MyWaitThread.java:40) 
     - locked <0xef63beb8> (a java.util.ArrayList) 
     at java.lang.Thread.run(Thread.java:595) 
    

synchronized____80">synchronized 的锁升级机制(偏向锁, 轻量级锁, 重量级锁)

entrySet 的机制被称为重量级锁,性能较差。jdk 1.6 从 JVM 层⾯对 synchronized 进行了⼤优化,引入了偏向锁, 轻量级锁和锁升级机制,提高了 synchronized 的性能。

偏向锁

  1. ObjectHeader 对象头信息中,有一个标记记录了此对象 monitor 的持有线程,如果尝试获取的线程和此对象 monitor 的持有线程相同,则直接获得 monitor,不需要走锁机制。体现了 synchronized 可重入锁的特征。

轻量级锁

  1. 当一个线程尝试进入临界区时,如果获取偏转锁失败,则进行轻量级锁的机制,CAS 自旋尝试抢锁。
  2. 这个设计是为了提高锁的性能,如果在 CAS 自旋期间抢到了锁,则可以减少线程休眠和苏醒的开销。

重量级锁

  1. 自旋 n 次(默认为10,可以通过 JVM 参数 PreBlockSpin 调整) 如果仍未抢到,则升级为重量级锁,线程进入 entrySet。entrySet 的机制如本文前部分所述。

http://www.niftyadmin.cn/n/209548.html

相关文章

文件上传高级用法文件复制

文件上传 spring 文件上传 导包 <!-- 文件上传--><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.11.0</version></dependency><dependency><groupId>c…

[计算机图形学]纹理的高级应用(前瞻预习/复习回顾)

一、前言 上节课我们讲了纹理的放大缩小产生问题后&#xff0c;我们的解决方法&#xff0c;那么纹理是什么呢&#xff1f;在现代GPU中&#xff0c;我们可以理解为是内存范围查询(滤波)&#xff0c;也就是对一块区域做点查询/范围查询&#xff0c;并且做的非常快&#xff0c;也…

MySQL 索引初步了解

MySQL 索引初步了解一、索引的类型二、创建索引的技巧所谓索引就是为特定的mysql字段进行一些特定的算法排序&#xff0c;比如二叉树的算法和哈希算法&#xff0c;哈希算法是通过建立特征值&#xff0c;然后根据特征值来快速查找。而用的最多&#xff0c;并且是mysql默认的就是…

4.8---知识复习之Springboot(复习版本)---每天一点点

1. 什么是 Spring Boot&#xff1f; Spring Boot 是 Spring 开源组织下的子项目,主要是简化了使用Spring 的难度&#xff0c;简省了繁重的配置&#xff0c;提供了各种启动器&#xff0c;使开发者能快速上手。 2. 为什么要用SpringBoot? 快速开发&#xff0c;快速整合&#…

BI Data Mining Case 保险反欺诈预测 Python

Dependencies import pandas as pd import numpy as np数据读取 train pd.read_csv(insurance/train.csv) test pd.read_csv(insurance/test.csv) train pd.concat([train,test]).reset_index(drop True)数据摸底、数据清洗 数据摸底 数据摸底&#xff1a;依据业务经验…

逍遥自在学C语言 | 位运算符的基础用法

前言 一、人物简介 第一位闪亮登场&#xff0c;有请今后会一直教我们C语言的老师 —— 自在。 第二位上场的是和我们一起学习的小白程序猿 —— 逍遥。 二、构成和表达方式 位运算符是一组用于在二进制数之间进行操作的运算符 运算符名称示例&位与a && b|位或a …

代码随想录算法训练营第四十九天| 121. 买卖股票的最佳时机、122. 买卖股票的最佳时机 II。

121. 买卖股票的最佳时机 题目链接&#xff1a;力扣 题目要求&#xff1a; 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖出该股票。设计一个算…

VR对比有哪些优势?全景对比适用于哪些领域?

俗话说“货比三家”&#xff0c;这样才能挑选出性价比较高的商品。如今&#xff0c;VR全景推出全新对比功能&#xff0c;让用户在线上就能对比商品各方面性价比&#xff0c;VR动态对比让商品差异化效果展示更加直观&#xff0c;VR对比区别于传统图片对比&#xff0c;通过同一屏…