java谜题--类初始化时容易死锁情况

Java防崩溃十诫:程序看了都沉默!从“空指针”到“内存爆表”,你中了几条? 你有没有想过?别急,今天咱不聊花里胡哨的框架,只聊一个硬核问题: Java程序崩溃的“十宗罪”+“十全十美”解决方案! 从空指针到线程死锁,从内存爆表到配置陷阱,带你穿越Java崩溃的终极答案!场景:对象未初始化就调用方法,程序直接“跪了”! 问题:如何优雅地避免空指针异常?代码实战: 核心原理:冷幽默:以前我写代码不加null判断,结果程序崩溃了,老板说“你这是在写代码,还是在‘自杀’?”场景:内存泄漏导致堆栈溢出,JVM直接“爆表”! 问题:如何定位并修复内存泄漏?代码实战: 核心原理:冷幽默:以前我写 阅读详情

谜题85:惰性初始化

下面这个可怜的小类实在是太懒了,甚至于都不愿意用通常的方法进行初始化,所以它求助于后台线程。这个程序会打印什么呢?每次你运行它的时候都会打印出相同的东西吗?
public class Lazy {
    private static boolean initialized = false;
    static {
        Thread t = new Thread(new Runnable() {
                public void run() {
                    initialized = true;
                }
            });
        t.start();
        try{
            t.join();
        }catch (InterruptedException e){
            throw new AssertionError(e);
        }
    }
 
    public static void main(String[] args){
        System.out.println(initialized);
    }
}

虽然有点奇怪,但是这个程序看起来很直观的。静态域initialized初始时被设为false。然后主线程创建了一个后台线程,该线程的run方法将initialized的值设为true。主线程启动了后台线程之后,就调用了join方法等待它的结束。当后台线程完成运行的时候,毫无疑问initialized的值已经被设为了true。当且仅当这个时候,调用了main方法的主线程会打印出initialized的值。如果是这样的话,程序肯定会打印出true吗?如果你运行该程序,你会发现它不会打印任何东西,它只是被挂起了。

为了理解这个程序的行为,我们需要模拟它初始化的细节。当一个线程访问一个类的某个成员的时候,它会去检查这个类是否已经被初始化。在忽略严重错误的情况下,有4种可能的情况[JLS 12.4.2]:

  • 这个类尚未被初始化。
  • 这个类正在被当前线程初始化:这是对初始化的递归请求。
  • 这个类正在被其他线程而不是当前线程初始化。
  • 这个类已经被初始化。

当主线程调用Lazy.main方法时,它会检查Lazy类是否已经被初始化。此时它并没有被初始化(情况1),所以主线程会记录下当前正在进行初始化,并开始对这个类进行初始化。按照我们前面的分析,主线程会将initialized的值设为false,创建并启动一个后台线程,该线程的run方法会将initialized设为true,然后主线程等待后台线程执行完毕。此时,有趣的事情开始了。

PS:t.start() 方法调用run(),并不是马上运行完,需要等待主线程完成初始化以后,再运行设为true

那个后台线程调用了它的run方法。在该线程将Lazy.initialized设为true之前,它也会去检查Lazy类是否已经被初始化。这个时候,这个类正在被另外一个线程进行初始化(情况3)。在这种情况下,当前线程,也就是那个后台线程,会等待Class对象直到初始化完成。遗憾的是,那个正在进行初始化工作的线程,也就是主线程,正在等待着后台线程运行结束。因为这2个线程现在正相互等待着,该程序就死锁了(deadlock)。这就是所有的一切,真是遗憾。有2种方法可以订正这个程序。到目前为止,最好的方法就是不要在类进行初始化的时候启动任何后台线程:有些时候,2个线程并不比1个线程好。更一般的讲,要让类的初始化尽可能地简单。订正这个程序的第2种方法就是让主线程在等待后台线程之前就完成类的初始化:

// Bad way to eliminate the deadlock. Complex and error prone
public class Lazy {
    private static boolean initialized = false;
    private static Thread t = new Thread(new Runnable() {
                    public void run() {
                            initialized = true;
                    }
            });
    static {
        t.start();
    }
    
    public static void main(String[] args){
        try{
            t.join();
        }catch (InterruptedException e){
            throw new AssertionError(e);
        }
        System.out.println(initialized);
    }
}

虽然这么做确实消除了死锁,但是它却是一个非常不好的想法。主线程需要等待后台线程完成工作,但是其他的线程不需要这么做。一旦主线程完成了对Lazy类的初始化,其他线程就可以使用这个类了。这使得在initialized的值还是false的时候,其他线程就可以观察到它。

总之,在类的初始化期间等待某个后台线程很可能会造成死锁。要让类初始化的动作序列尽可能地简单。类的自动初始化被公认为是语言设计上的难题,Java的设计者们在这个方面做得很不错。如果你写了一些复杂的类初始化代码,很多种情况下,你这是在搬起石头砸自己的脚。

SpringBoot启动卡住无日志?三步定位线程阻塞点与解决方案 在Java应用开发中,线程阻塞是常见的性能与稳定性问题,尤其在SpringBoot这类复杂框架的启动阶段。其原理通常是某个线程在等待资源(如锁、I/O、网络连接)时被挂起,导致整个应用流程停滞。理解线程状态(RUNNABLE、WAITING、BLOCKED)和线程转储分析技术,对于诊断生产环境中的“静默”故障具有重要价值。通过jstack等工具获取线程快照,可以精准定位到数据库连接池、配置中心客户端或自定义初始化代码中的阻塞点,这是后端工程师必备的线上调试技能。本文以SpringBoot启动卡住的典型场景为 阅读详情

相关推荐

Java编程谜题解析及源代码大全

本文还有配套的精品资源,点击获取 简介:《Java解惑全书》通过95个编程谜题深入探讨了Java的类型系统、内存模型、异常处理、多线程和集合框架等核心概念。源代码压缩包"java-puzzlers"提供了所有谜题的实现,便于读者运行和分析,从而加深对Java特性的理解。本书有助于提高Java开发者的编程技巧,并避免常见错误。 1. Java类型系统深入解析 ...

weixin_42443533的博客 817

代码审计的规范参考(非常详细)零基础入门到精通,收藏这一篇就够了

具体而言,通过审计发现源代码层面的安全弱点,但不包含软件分析、设计、测试、应用部署等层面的安全弱点。审计人员应检查代码是否将敏感数据存储在没有被锁定或被错误锁定的内存中,(将敏感数据存储于加锁不恰当的内存区域,可能会导致该内存通过虚拟内存管理器被写入到在磁盘上的交换文件中,从而使得数据更容易被外部获取),如果结果为肯定,则提示存在安全风险;审计实施结束后,组织现场审计结束会,将初始审计结果提供给被审计项目成员组,提供被审计项目组澄清误解的机会,并允许项目成员提供其他需要补充的信息(如审计员未考虑到的)。

Libra1313的博客 5001

保姆级教程:手把手教你用Python逆向分析极验4.0滑块验证码的w参数生成逻辑

本文提供了一份详细的Python逆向工程教程,手把手教你分析极验4.0滑块验证码的w参数生成逻辑。通过HTTP请求分析、JavaScript逆向和加密算法复现,帮助开发者深入理解验证码防护机制,并实现高效的自动化解决方案。教程涵盖AST反混淆、轨迹生成算法和AES加密等核心技术,适合中高级开发者提升逆向工程能力。

weixin_30172941的博客 304

java多线程-类初始化场景

java多线程-类初始化场景 摘自《JVM高级特性与最佳实践》 java 必须立即对类进行“初始化” 的场景 遇到 new、getstatic、putstatic或invokestatic 这四条字节码指令时,如果类型没有进行过初始化,则需要先触发其初始化阶段。能够生成这四条指令的典型Java代码场景有: 使用new关键字实例化对象的时候 读取或设置一个类型的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候 调用一个类型的静态方法的时候。 使用 java.lang.r

面朝大海,春暖花开 303

Flink 作业报错——TaskManager连接不上

Flink 作业报错——TaskManager连接不上

lm223的博客 1900

BMW Standard Tools 宝马FSC工具套装下载

这是一款非常专业的一套宝马编码工具,懂行的人自然知道他的用处,不懂行的人,我解释再多也不会明白的,需要的拿去吧。软件名:BMW Standard Tools包含工具:BMW Coding Tool v2.50、change Language、CIC Mafia、Dr.Gini B021、EasyConnect、EDIABAS Konfigurator v1.1、EDIABAS Konfigurator v2.0.0、FSC-Tools_2.18、FSTool_v2_CarServer、INPA、ITool

Zj_arrow的博客 6998

41 父类持有子类的引用多线程加载造成死锁问题

前言 之前在 某技术交流群 中看到了这样的一个问题 遇到一个没想明白的问题。 一个类的静态字段是当前类的子类,多线程同时实例化该类和子类时,线程卡住。 改问题的作者将问题记录在了他的 bloghttps://www.yuque.com/buildup/java/tzemks 呵呵 这应该是几周之前的问题了, 拿出来 遛一遛 如果 您知道 类初始化 的一些流程的话, 那这个问题 应该还是比较简单 然后 我们这里调试一下 这里的具体的情况 以下截图, 调试流程基于......

970655147的专栏 500

熵权法+TOPSIS完整教学:用Python评估大学科研实力(含雷达图绘制技巧)

本文提供了一套完整的熵权法与TOPSIS综合评价方法在Python中的实现教程,以高校科研实力评估为例,详细讲解了从数据预处理、客观权重计算、综合排序到结果可视化的全流程。重点介绍了如何利用Python进行数据清洗与转换,通过熵权法自动确定指标权重,并结合TOPSIS模型计算各院校与理想解的相对接近度进行排名。教程还包含了使用Matplotlib绘制专业雷达图的技巧,帮助读者直观呈现多维数据的对比分析结果,适用于各类多指标决策与绩效评估场景。

weixin_28520495的博客 416

java类静态初始化死锁问题

3.exec-2阻塞在DriverManager的静态初始化过程执行loadInitialDrivers()方法中,该方法会完成对Driver所有的实现类的加载和初始化,也就是在该过程中会进行了org.postgresql.Driver的初始化,那么就和现象2中的逻辑冲突,从而导致死锁;1.大多数线程都是阻塞在Class.forName,因为Class.forName会调用native方法对类进行静态初始化,该初始化过程是加锁的,主要是为了避免重复对类进行加载;2.exec-3阻塞在。

heyiwuwu的博客 471

保姆级教程:用LoRA微调Chinese-Mistral-7B模型,并一键部署到Ollama(附完整代码)

本文提供了一份详细的LoRA微调Chinese-Mistral-7B模型的保姆级教程,涵盖环境配置、数据预处理、微调实战、模型量化及Ollama部署全流程。通过实战案例和完整代码,帮助开发者快速构建专属中文大模型,适用于医疗、法律等垂直领域。

weixin_30369041的博客 249

MiFlash Prime免授权刷机实战:解锁小米9008 EDL救砖新方案

MiFlash Prime作为免授权刷机工具,有效解决了小米设备在9008 EDL模式下因账号验证无法救砖的难题。本文详细介绍了从驱动安装、进入EDL模式到固件刷写的完整流程,帮助用户安全快速地恢复变砖设备,是维修人员和极客的实用解决方案。

5f4d3s2a1q的博客 2875

多线程笔记--3

18, 保证共享变量的发布是安全的 a, 通过静态初始化器初始化对象 (jls 12.4.2 叙述 , jvm 会保证静态初始化变量是同步的 ) b, 将对象申明为 volatile 或使用 AtomicReference c, 保证对象是不可变的 d, 将引用或可变操作都由锁来保护 19, 设计线程安全的类 , 应该包括的基本要素 : a, 确定哪些是可变共享变量 b, 确定...

知行天下 132

某物data魔改与变种算法还原(含加解密Py纯算)

某物data加解密、设备指纹魔改、变异、自定义加密分析与纯算还原

吴秋霖的博客 1281

谜题85:惰性初始化

下面这个可怜的小类实在是太懒了,甚至于都不愿意用通常的方法进行初始化,所以它求助于后台线程。这个程序会打印什么呢?每次你运行它的时候都会打印出相同的东西吗? public class Lazy { private static boolean initialized = false; static { Thread t = new Thread(new Runnable() { pu...

weixin_30533797的博客 109

机器人控制基础:伺服驱动器的泄放电阻

摘要:伺服驱动器泄放电阻用于消耗电机减速或制动时产生的再生电能,防止母线电压过高损坏元件。其核心作用是将多余电能转化为热能,规避硬件损坏和停机风险。选型需关注功率、阻值和封装三大参数,功率应匹配再生能量,阻值需符合驱动器要求,封装应满足散热需求。可通过动态监测母线电压或静态检测电阻阻值来判断其是否正常工作。

start_up_go的博客 381

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Java谜题畅读版之更多的库谜题

谜题78:反射的污染 这个谜题举例说明了一个关于反射的简单应用。这个程序会打印出什么呢? [java]view plaincopyprint? importjava.util.*; importjava.lang.reflect.*; publicclassReflector{ publicstaticvoidmain(String[]ar...

子谦 117

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

【Scala谜题】继承

笔记来源:Scala谜题 多级继承 Scala 支持面向对象的编程概念,继承是它的一个很重要的特征。继承通常对父类和特质中定义的缺省值的重载很有用。当增加多级继承时事情变得更加有趣,例如下面这段程序。 trait A { val foo: Int val bar = 10 println("In A: foo: " + fo...

weixin_34257076的博客 162

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

码界奇缘 Java 觉醒 第七章 多线程矩阵

生成的线程快照——显示名为"Virus_Replicator"的守护线程仍在活动!“看这AQS的地下齿轮!(地面透明化展现CLH队列)每个等待线程化作齿轮齿,写是红色光路起点(指向镜面折射点),锁释放是蓝色光路枢纽,陆小柒踏入由线程交织成的银色立方体空间,空中漂浮着闪烁的。优化后线程化作光粒,在用户态轻量级切换。旋转齿轮(演示CAS火花)…当死锁解除时,空中突然浮现由。是常驻工匠(指向基础线程),是临时工(浮现伸缩线程),调用是绿色初始化光束…规则就像光路校准器?线程池引发的内存黑洞。

weixin_43236925的博客 476

码界奇缘 Java 觉醒 后记 第十五章 Project Leyden - 静态宇宙的永恒牢笼

注解(冰晶浮现新的纹路),在编译期生成所有可能的代理类——哪怕这会让镜像体积膨胀百倍!“在此宇宙,时间只有两态:编译时(展示冰封的代码瀑布)与运行时(瞬移光束)。正是隐藏恶意代码的最佳外衣…在静态宇宙运行被禁止的动态代码。“那接口代理如何实现?

weixin_43236925的博客 473

Java解惑八:很多其它库之谜

本文是依据JAVA解惑这本书,做的笔记。 电子书见:http://download.csdn.net/detail/u010378705/7527721 谜题76 将线程的启动方法start(),写成了run(); PS:管程(monitor)锁有待进一步理解。 谜题77 线程中锁的问题。 理解不深刻。 ...

weixin_30402343的博客 106

Java解惑八:更多库之谜

本文是根据JAVA解惑这本书,做的笔记。 电子书见:http://download.csdn.net/detail/u010378705/7527721 谜题76 将线程的启动方法start(),写成了run(); PS:管程(monitor)锁有待进一步理解。 谜题77 线程中锁的问题。 理解不深刻。 谜题78 反射会造成访问

千寻的专栏 758

java并发基础(五)--- 线程池的使用

第8章介绍的是线程池的使用,直接进入正题。一、线程饥饿死锁和饱和策略  1.线程饥饿死锁  在线程池中,如果任务依赖其他任务,那么可能产生死锁。举个极端的例子,在单线程的Executor中,如果一个任务提交了另一个任务到相同的Executor中,并等待其返回,那么就会发生死锁。第二个任务停留在工作队列中,第一个又一直等待(因为是单线程)。这块记住一个信息,就是如果线程池中的任务是互相依赖的,除非线...

lengyue309的博客 187

Java高级面试10大高频送命题深度解析:从String到ThreadLocal的底层原理

在Java开发领域,理解核心机制底层原理是区分普通开发者与高级工程师的关键。从内存模型到并发编程,Java语言规范定义了对象创建、方法调用和线程交互的基本规则。这些原理决定了程序的性能、稳定性和可维护性,是构建高并发、高可用系统的技术基石。例如,String常量池机制通过复用不可变对象优化内存使用,而volatile关键字则通过内存屏障保证多线程间的可见性。在实际应用场景中,HashMap的快速失败机制防止了迭代过程中的数据不一致,ThreadLocal的弱引用设计则需要配合remove()调用来避免线程池

weixin_34062469的博客 353

Debugging App Launch Timeouts: A Deep Dive into Failed to Spawn Errors

本文深入探讨了Android应用启动时常见的“Failed to spawn”超时错误。文章分析了导致应用启动超时的核心原因,包括应用自身初始化过慢或死锁、系统级限制(如USAP、SELinux)以及调试工具(如Frida)的兼容性问题,并提供了从日志分析到针对性解决方案的完整实战调试手册,帮助开发者有效定位并解决这一棘手问题。

weixin_29200485的博客 323
上一篇: ibatis 实现一对多代码
下一篇: java谜题--java运行时修改引用类的静态常量
feiyu8607
博客等级 码龄18年 26粉丝 51原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值