java类静态初始化死锁问题

news2024/12/24 2:34:51

问题

前端时间帮同事分析了一个IO线程阻塞问题,该问题导致服务端无法处理任何请求,只能进行重启解决;事发时运维dump了下栈信息,堆栈信息如下图:

从上面可以看到io线程都阻塞于Object.wait(),具体是执行Class.forName()进行类加载时导致的阻塞,但是其中有两个线程阻塞点不一样,分别如下图:

分析

从问题图片可以看出:这两个阻塞点都和DriverManager的静态方法有关系,其中exec-2线程阻塞在DriverManager的静态代码初始化过程中,代码如下:

public class DriverManager {


    // List of registered JDBC drivers
    private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
    private static volatile int loginTimeout = 0;
    private static volatile java.io.PrintWriter logWriter = null;
    private static volatile java.io.PrintStream logStream = null;
    // Used in println() to synchronize logWriter
    private final static  Object logSync = new Object();

    /* Prevent the DriverManager class from being instantiated. */
    private DriverManager(){}


    /**
     * Load the initial JDBC drivers by checking the System property
     * jdbc.properties and then use the {@code ServiceLoader} mechanism
     */
    static {
        loadInitialDrivers();//阻塞点
        println("JDBC DriverManager initialized");
    }
    ......................
}

而exec-3线程阻塞在调用DriverManager.registerDriver(),代码如下:

public class Driver{

 static {
    try {
      // moved the registerDriver from the constructor to here
      // because some clients call the driver themselves (I know, as
      // my early jdbc work did - and that was based on other examples).
      // Placing it here, means that the driver is registered once only.
      register();
    } catch (SQLException e) {
      throw new ExceptionInInitializerError(e);
    }
  }
.......
public static void register() throws SQLException {
    if (isRegistered()) {
      throw new IllegalStateException(
          "Driver is already registered. It can only be registered once.");
    }
    Driver registeredDriver = new Driver();
    DriverManager.registerDriver(registeredDriver);//line727,阻塞点
    Driver.registeredDriver = registeredDriver;
  }
.....
}

以此依据来分析下现象:

1.大多数线程都是阻塞在Class.forName,因为Class.forName会调用native方法对类进行静态初始化,该初始化过程是加锁的,主要是为了避免重复对类进行加载;

2.exec-3阻塞在org.postgresql.Driver类静态初始化过程(static代码块)中的对DriverManager.registerDriver()方法的调用中,说明其在等待DriverManager的静态初始化完成;

3.exec-2阻塞在DriverManager的静态初始化过程执行loadInitialDrivers()方法中,该方法会完成对Driver所有的实现类的加载和初始化,也就是在该过程中会进行了org.postgresql.Driver的初始化,那么就和现象2中的逻辑冲突,从而导致死锁;

因此大致可以判断整个死锁过程应该是类加载死锁问题,该死锁问题最大的一个特征就是:两个类在进行类初始化过程中,会对对方进行一次类加载,如果这两个类同时进行类初始化,就会出现死锁;

验证

验证代码:

package test.common;



public class 	StaticInitialDeadLock {

	
	
	
	public static void main(String[] args) throws InterruptedException {

       new Thread(()->{
    	   try {
			Class.forName("test.common.TestA");
		} catch (Exception e) {
		}
       }).start();
       new Thread(()->{
    	   try {
    		  Class.forName("test.common.TestB");
   		} catch (Exception e) {
   		}
       }).start();
       Thread.sleep(100000);
	}
}

class TestA {
	static {
		
		try {
			System.out.println("pga initial");
			Thread.sleep(1000);
			Class.forName("test.common.TestB");
		     System.out.println("pga initial finish");
		} catch (Exception e) {
		}
	}
	public static void test() {
		System.out.println("pga");
	}
}

class TestB {
	
	static {
		
		try {
			System.out.println("pgb initial");
			Thread.sleep(1000);
			Class.forName("test.common.TestA");
			System.out.println("pgb initial finish");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2257750.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

厦门凯酷全科技有限公司怎么样?

随着短视频和直播带货的兴起&#xff0c;抖音电商平台迅速崛起&#xff0c;成为众多品牌和商家争夺的新战场。在这个竞争激烈的市场中&#xff0c;如何抓住机遇、实现销售增长&#xff0c;成为了每个企业面临的挑战。厦门凯酷全科技有限公司&#xff08;以下简称“凯酷全”&…

微信小程序uni-app+vue3实现局部上下拉刷新和scroll-view动态高度计算

微信小程序uni-appvue3实现局部上下拉刷新和scroll-view动态高度计算 前言 在uni-appvue3项目开发中,经常需要实现列表的局部上下拉刷新功能。由于网上相关教程较少且比较零散,本文将详细介绍如何使用scroll-view组件实现这一功能,包括动态高度计算、下拉刷新、上拉加载等完整…

在Windows和Ubuntu上安装SDKMAN

文章目录 1. SDKMAN概述2. 安装与使用SDKMAN2.1 在Windows上安装SDKMAN2.1.1 安装Git for Windows2.1.2 安装SDKMAN 2.2 利用SDKMAN管理Java2.2.1 查看所有可用的OpenJDK发行版2.2.2 安装Java2.2.3 查看Java版本2.2.4 shell指定使用某个Java版本 2.3 在Ubuntu上安装SDKMAN2.3.1…

1210 作业

思维导图 作业 使用read和write函数拷贝文件&#xff0c;一半拷进一个文件&#xff0c;另一半拷进另一个文件 #include <myhead.h> int main(int argc, const char *argv[]) {int fd1 open("./z1.txt",O_RDONLY);if(fd1-1){perror("open");return…

牛客网热门Java面试题及答案整理(2024最新版)

当今互联网行业中&#xff0c;Java 作为一种广泛应用的编程语言&#xff0c;对于求职者来说仍是一项受欢迎的技能。然而&#xff0c;随着市场上的开发人员数量越来越多&#xff0c;Java 面试的竞争也愈加激烈。 目前 Java 面试有着以下现状&#xff1a; 面试难度加大与过去相…

【SSH+X11】VsCode使用Remote-SSH在远程服务器的docker中打开Rviz

&#x1f680;今天来分享一下通过VsCode的Remote-SSH插件在远程服务器的docker中打开Rviz进行可视化的方法。 具体流程如下图所示&#xff0c;在操作开始前&#xff0c;请先重启设备&#xff0c;排除之前运行配置的影响&#xff1a; ⭐️ 我这里是使用主机连接服务器&#xff…

iPhone 17 Air基本确认,3个大动作

近段时间&#xff0c;果粉圈都在讨论一个尚未发布的新品&#xff1a;iPhone 17 Air&#xff0c;苹果又要来整新活了。 从供应链消息来看&#xff0c;iPhone 17 Air本质上是Plus的替代品&#xff0c;主要是在维持“大屏”这一卖点的同时&#xff0c;增加了“轻薄”属性&#xff…

浅析OCR技术与大模型的深度融合—中安未来OCR产品优势及前景探索

OCR&#xff08;光学字符识别&#xff09;技术作为一种文本识别工具&#xff0c;已在文档管理、自动化办公和图书数字化等领域发挥了重要作用。然而&#xff0c;随着深度学习和大语言模型&#xff08;LLM&#xff09;的迅猛发展&#xff0c;OCR技术迎来了新的机遇和挑战。如今&…

Android四大组件——Activity(二)

一、Activity之间传递消息 在&#xff08;一&#xff09;中&#xff0c;我们把数据作为独立的键值对进行传递&#xff0c;那么现在把多条数据打包成一个对象进行传递&#xff1a; 1.假设有一个User类的对象&#xff0c;我们先使用putExtra进行传递 activity_demo06.xml <…

STM32G4系列MCU双ADC多通道数据转换的应用

目录 概述 1 STM32Cube配置项目 1.1 基本参数配置 1.1.1 ADC1参数配置 1.1.2 ADC2参数配置 1.2 项目软件架构 2 功能实现 2.1 ADC转换初始化 2.2 ADC数据组包 3 测试函数 3.1 Vofa数据接口 3.2 输入数据 4 测试 4.1 ADC1 通道测试 4.2 ADC2 通道测试 概述 本文…

STM32串口接收与发送(关于为什么接收不需要中断而发生需要以及HAL_UART_Transmit和HAL_UART_Transmit_IT的区别)

一、HAL_UART_Transmit和HAL_UART_Transmit_IT的区别 1. HAL_UART_Transmit_IT&#xff08;非阻塞模式&#xff09;&#xff1a; HAL_UART_Transmit_IT 是非阻塞的传输函数&#xff0c;也就是说&#xff0c;当你调用 HAL_UART_Transmit_IT 时&#xff0c;它不会等到数据完全发…

constexpr、const和 #define 的比较

constexpr、const 和 #define 的比较 一、定义常量 constexpr 定义&#xff1a;constexpr用于定义在编译期可求值的常量表达式。示例&#xff1a;constexpr int x 5;这里&#xff0c;x的值在编译期就确定为5。 const 定义&#xff1a;const表示变量在运行期间不能被修改&…

BMS电池管理系统

一.项目简介 1.该项目是基于BQ7692003PWR STM32F103C8T6研发的一块锂电池控制板&#xff0c;本控制板可供五串18650锂电池&#xff08;目前软件仅支持三元锂类型&#xff0c;标称电压为4.2V&#xff09;串联使用&#xff0c;电芯均衡采用被动均衡方式 二.本项目功能 1.监控任…

Milvus向量数据库01-基础概念

Milvus向量数据库01-基础概念 Zilliz Cloud 集群由全托管 Milvus 实例及相关计算资源构成。您可以在 Zilliz Cloud 集群中创建 Collection&#xff0c;然后在 Collection 中插入 Entity。Zilliz Cloud 集群中的 Collection 类似于关系型数据库中的表。Collection 中的 Entity …

【OpenCV】模板匹配

理论 模板匹配是一种在较大图像中搜索和查找模板图像位置的方法。为此&#xff0c;OpenCV 带有一个函数 cv.matchTemplate&#xff08;&#xff09; 。它只是在输入图像上滑动模板图像&#xff08;如在 2D 卷积中&#xff09;&#xff0c;并比较模板图像下的模板和输入图像的补…

深入解析下oracle的number底层存储格式

oracle数据库中&#xff0c;number数据类型用来存储数值数据&#xff0c;它既可以存储负数数值&#xff0c;也可以存储正数数值。相对于其他类型数据&#xff0c;number格式的数据底层存储格式要复杂得多。今天我们就详细探究下oracle的number底层存储格式。 一、环境搭建 1.…

MySQL Binlog 日志监听与 Spring 集成实战

MySQL Binlog 日志监听与 Spring 集成实战 binlog的三种模式 MySQL 的二进制日志&#xff08;binlog&#xff09;有三种常见的格式&#xff1a;Statement 模式、Row 模式和Mixed 模式。每种模式的设计目标不同&#xff0c;适用于不同的场景&#xff0c;以下是它们的详细对比和…

Vmware Vcenter7.0证书web续期发生错误

1. 故障描述 vSphere Client 版本 7.0.2.00200 vCenter _MACHINE_CERT快到期了&#xff0c;通过web界面更新证书失败 第一步先这样&#xff0c;重新续订一下证书 续订发生错误 2. 解决办法 2.1. 前提工作 登陆ssh到vcenter&#xff0c;重新生成证书 先关掉HA&#xff…

Oracle报错ORA-01653: 表xx无法通过 8192在表空间中扩展

向Oracle 19g数据库中批量插入数据&#xff0c;当插入近2亿条数据后&#xff0c;报出如下错误&#xff1a; ORA-01653: 表xx无法通过 8192 (在表空间 xx_data 中) 扩展 查看表空间&#xff0c;发现表空间大小已达到32G&#xff0c;表空间无法进行自动扩展了。&#xff08;初始…

数据结构(3)单链表的模拟实现

上一节我们进行了数据结构中的顺序表的模拟式现&#xff0c;今天我们来实现一下另外一个数据结构&#xff1a;单链表。 我们在实现顺序表之后一定会引发一些问题和思考&#xff1a; 1.顺序表在头部和中间插入数据会用到循环&#xff0c;时间复杂O&#xff08;N&#xff09; …