多线程陷阱(所有静态初始化块中的代码不一定是类初始化操作)

多线程中局部静态变量初始化的陷阱     C++当中常常需要一个全局唯一的对象实例,这时候,我们就会想到单件模式。如何实现这一模式?全局变量当然是一个简单可行的方法,然而,这太丑陋。嗯,其实,丑陋倒也罢了,最严重的是它将引诱程序员滥用全局变量,这将导致维护的灾难。    既然全局变量是可能有害的,那么,我们我们把它隐藏一下,放到某个类当中去,作为类的静态数据成员。这看上去不错,我也这么认为。当我们只是简单的需要一个全局对象时,这很 阅读详情
大家先看一个程序:
public class StaticThreadInit {
	static {
		Thread t = new Thread() {
			public void run() {
				System.out.println("进入run方法");
				System.out.println("1------" + website);
				website = "www.leegang.org";
				System.out.println("2------" + website);
				System.out.println("退出run方法");
			}
		};
		t.start();
		try {
			t.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	static String website = "www.crazyit.org";

	public static void main(String args[]) {
		System.out.println("main:" + StaticThreadInit.website);
	}
}
运行结果:
进入run方法
为什么会这样呢?website的值为什么没打印出来呢?下边我们来分析下这段程序的执行过程:
    main线程试图访问StaticThreadInit.website的值,此时StaticThreadInit尚未被初始化,因此main线程开始对该类执行初始化。初始化过程主要分为两个步骤:一是为该类所有静态field分配内存,二是调用静态初始化块的代码执行初始化。因此main线程首先会为StaticThreadInit类的website field分配内存空间,此时的website的值为null,接着,main线程开始执行StaticThreadInit类的静态初始化块。该代码块创建并启动一个新的线程,并调用了新线程的join()方法,这意味着main线程必须等待新线程执行结束后才能向下执行。新线程开始执行之后,首先执行System.out.println("进入run方法");代码,这就是运行该程序时看到的第一行输出。接着程序试图执行System.out.println(website);问题出现了,StaticThreadInit类正由main线程执行初始化,因此新线程会等待main线程对StaticThreadInit类执行初始化结束。这时候就满足了死锁条件:两个线程互相等待对方执行,因此都不能向下执行。因此程序执行到此就出现了死锁,程序没法执行下去了。

 

出现死锁的关键原因是程序调用了t.join()。下面分析将t.join()注释后的程序运行情况:

main:www.crazyit.org

进入run方法

1------www.crazyit.org

2------www.leegang.org

退出run方法

下面分析下执行过程:

main线程进入StaticThreadInit静态初始化之后,同样创建并启动新的线程,这次主线程不会等待新线程,此时新线程只是处于就绪状态,还未进入运行状态。main线程继续执行初始化操作,当完成初始化操作之后,main中的输出语句也就可以完成了。接下来新线程才进入运行状态,一次执行run方法。很明显,产生上面运行结果的原因是调用一条线程的start()方法后,该线程并不会立即进入运行状态,他将只是保持在就绪状态。

为了改变这种状态,在t.start()之后立即调用Thread.sleep()暂停当前程序线程,使得新线程立即获得执行的机会,运行结果:

进入run方法

main:www.crazyit.org

1------www.crazyit.org

2------www.leegang.org

退出run方法

下面分析下执行过程:

当main线程创建启动一条新的线程时,然后主线程调用Thread.sleep()暂停自己,使得新线程获得执行机会,但当新线程执行输出website的值时,因为StaticThreadInit类还未初始化完成,因此新线程不得不放弃执行。线程调度器再次切换到main线程,mian线程于是完成website的初始化,至此StaticThreadInit类初始化完成。通常main线程不会立即切换回来执行新的线程,它会执行main方法里的第一行代码。

这里有一个实际问题:静态初始化块里多线程对静态field所赋的值根本不是初始值,它只是一次普通的赋值。再看下边的代码:
    
public class StaticThreadInit {
	static {
		Thread t = new Thread() {
			public void run() {
				website = "www.leegang.org";
			}
		};
		t.start();
		try {
			Thread.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	final static String website;
	public static void main(String args[]) {
		System.out.println("main:" + StaticThreadInit.website);
	}
}
编译上面的程序就会出错:无法为最终变量website指定值。

 

从上面的错误提示可以看出:静态初始化块启动的新线程根本不允许为website赋值,这表明,新线程为website的赋值根本不是初始化操作,只是一次普通的赋值。这个程序给我们的教训是:分析一个程序不能仅仅停留在静态的代码上,而是应该从程序执行过程来把握程序的运行细节。

不要认为所有放在静态初始化块中的代码就一定是类初始化操作,静态初始化块中启动新线程的run方法只是新线程的线程执行体,并不是类初始化操作。类似地,不要认为所有在非静态初始化块中的代码就一定是对象初始化操作,非静态初始化块中启动新的线程的run方法只是新线程的线程执行体,并不是对象初始化操作。

VS2022 中 std::mutex 静态初始化陷阱:一次诡异崩溃的排查记录 VS2022中使用std::mutex时遇到的静态初始化陷阱问题。该问题源于VS2022 17.4版本为std::mutex添加constexpr支持后,全局静态初始化可能导致系统资源未就绪时锁失效或崩溃。文章分析了问题现象、影响范围,并提供了解决方案。最后给出了排查清单和核心建议 阅读详情

相关推荐

HALCON多线程编程中的静态变量陷阱:如何避免内存浪费和线程冲突

本文深入剖析了HALCON多线程编程中静态变量引发的内存浪费与线程冲突两大核心陷阱。通过解析HALCON的内存管理机制与多线程架构,文章提供了从避免静态变量、使用线程局部存储,到实施安全同步与资源池模式等实战解决方案,旨在帮助高级开发者构建高效、稳定的高性能机器视觉系统。

android的专栏 883

Java多线程中static变量总结

参考文章:Java多线程中static变量的使用 (一)静态变量:线程非安全 1、静态变量:使用static关键字定义的变量。static可以修饰变量和方法,也有static静态代码块。被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享。只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象。 用public修饰的static成员变量...

justlpf的专栏 3045

C++多线程编程:局部静态变量线程安全初始化详解

在多线程编程中,线程安全是确保程序正确性的核心概念,它涉及数据竞争、原子操作和同步机制等基础原理。C++作为系统级编程语言,其内存模型和初始化语义直接影响并发程序的可靠性。局部静态变量的延迟初始化在单例模式等场景中广泛应用,但在多线程环境下,非原子化的初始化过程可能导致未定义行为,从而引发程序崩溃或数据不一致。C++11标准引入的“魔法静态变量”机制,通过编译器自动注入同步原语,从根本上解决了这一问题,确保了初始化操作的唯一性和线程安全。本文结合双检锁模式、std::call_once等实践技术,深入解析局

weixin_33428613的博客 327

如何保证static成员变量在多线程环境下存在的线程安全问题

2.1、可见性:当一个变量被声明为volatile时,所有线程都可以看到最新的值,即使这个值是在另一个线程中修改的。这是因为使用volatile修饰的变量会直接从主内存中读取,而不是从线程的本地缓存中读取,从而避免了线程之间可能存在的数据不一致性问题。总而言之,volatile关键字用于修饰变量,提供了可见性和禁止指令重排序的特性,用于在多线程环境下确保变量访问的正确性。它的主要含义是告诉编译器和处理器,在访问该变量时需要进行一些特殊的处理,以确保线程之间对该变量的操作是可见的。

weixin_42810017的博客 1020

Unity中5自由度机械臂运动控制与夹爪交互仿真实践

本文详细介绍了在Unity中实现5自由度机械臂运动控制与夹爪交互仿真的完整流程。从模型导入、物理组件配置到关节运动控制算法(包括逆运动学IK),再到夹爪抓取逻辑和工业分拣案例实战,提供了可落地的代码示例与性能优化方案,助力开发者快速掌握机械臂仿真核心技术。

weixin_29021757的博客 446

STM32+W5500实战:5分钟搞定FreeModbus TCP移植(附完整代码)

本文详细介绍了如何在STM32微控制器上,结合W5500以太网芯片,快速完成FreeModbus TCP协议栈的移植,构建稳定可靠的工业级Modbus TCP服务器。内容涵盖硬件连接、软件架构、驱动适配、核心移植代码实现及调试优化技巧,旨在提供可直接应用于实际项目的完整解决方案。

life6的博客 878

Java多线程中static变量的使用

线程,是我们项目中绕不过的重点领域。提到线程,就常会听到线程安全的术语。那什么是线程安全呢?通俗点说,就是线程访问时不产生资源冲突。其实,这是一个有点难以定义的概念,不是很容易让人一听就懂的概念。“一个类可以被多个线程安全调用就是线程安全的”《Java并发编程实践》。 来说说静态变量、实例变量、局部变量在多线程下的安全问题吧! (一)静态变量:线程非安全 1、静态变量:...

weixin_30488085的博客 842

libiec61850实战:手把手教你用C语言动态获取IED设备模型(附完整代码解析)

本文详细介绍了如何使用libiec61850库通过C语言动态获取IED设备模型,包括环境配置、连接建立、模型遍历及错误处理等关键步骤。通过完整的代码示例和实战案例,帮助开发者掌握智能变电站通信中的核心技术,提升工业自动化系统的开发效率。

weixin_42519985的博客 433

C++多线程对于类的静态成员处理

前期疑问:未完待续。

晴山の博客 956

kali2020.2安装出现的问题并附详细解决办法

kali2020.2的安装

weixin_48776804的博客 3047

SAP FICO实操:如何修改已发生业务数据的成本中心公司代码(附RKACOR06程序运行指南)

本文详细解析了SAP FICO系统中使用RKACOR06程序修改已发生业务数据的成本中心和公司代码的实操方法。通过实战案例分享,提供从前期准备到后期验证的全流程指南,帮助企业应对组织架构重组或财务核算体系调整时的数据迁移挑战,特别适用于跨公司代码的成本中心划转场景。

weixin_27303545的博客 189

多线程调用static方法线程安全问题

最近在工作中遇到了线程安全的问题,是在一个方法中调用了静态方法解析Date的字符串。 因为 SimpleDateFormat这个类是线程不安全的,所以不能在静态方法中定义全局的成员变量。 @Test void contextLoads() { ExecutorService executorService= Executors.newFixedThreadPool(6); for (int i = 0; i < 6; i++) { Runnable runnabl

m0_37602227的博客 5697

【液晶专题】从外挂到In-Cell/ On-Cell/ OGS再也不会傻傻分不清楚了

触控技术经历了从外挂式到内嵌式的革新,主要方案包括外挂式(Out-Cell)、OGS、On-Cell和In-Cell。

一点硬件 1252

【转】多线程陷阱(所有静态初始化块中的代码不一定是类初始化操作)

大家先看一个程序: [java] view plaincopyprint? <spanstyle="font-family:SimHei;">publicclassStaticThreadInit{ static{ Threadt=newThread(){ publicvoid...

weixin_30519071的博客 89

Android Camera Sensor HDR详解

本文详细介绍了Android相机传感器(Sensor)中实现高动态范围(HDR)采集的各种技术方式。主要内容包括: Sensor HDR技术分类 帧内HDR(Single-Frame HDR):在同一帧同一像素内完成多增益采集,无运动伪影和帧率损失,包括DAG(双模拟增益)、DCG(双转换增益)等技术 帧间HDR(Multi-Frame HDR):通过多帧曝光实现,如DOL(数字重叠)、Staggered HDR等 混合HDR:结合多种技术的复合方案 详细技术原理 重点解析了DAG和DCG两种主流帧内HDR

DU_YULIN的博客 358

地平线J6的基础简介

地平线J6M芯片:面向中阶辅助驾驶市场的高性价比芯片

weixin_43084908的博客 4447

Java表达式的陷阱——多线程的陷阱

6、多线程的陷阱        Java语言提供了非常优秀的多线程支持,使得开发者能以简单的代码来创建、启动多线程,而且Java语言内置了多线程支持极好地简化了多线程编程。虽然如此,Java多线程编程中依然存在一些容易混淆的陷阱。 6、1 不要调用run()方法        从Java5开始,Java提供了三种方式来创建、启动多线程。 继承Thread类创建线程类,重写run()

Never_Blue的博客 847

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

【C++静态成员初始化深度解析】:揭秘类外初始化的5大陷阱与最佳实践

掌握静态成员的类外初始化关键技巧,深入解析C++中静态成员变量在类外定义与初始化的正确方法。涵盖常见错误、作用域规则及头文件避免重复定义的实践方案,提升代码稳定性与可维护性,值得收藏。

LiteTrans的博客 434

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

小心处理 C++ 静态变量中的陷阱

static。

qq_42896106的博客 627

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

多线程安全:静态成员变量与原子操作的隐秘陷阱

本文深入探讨C++多线程编程中静态成员变量与原子操作的隐秘陷阱,分析数据竞争、死锁等常见问题及其解决方案。针对嵌入式系统开发,提供内存约束、实时性要求和能效考量的优化策略,帮助开发者编写更安全高效的多线程代码。

stone的博客 180

JVM的BUG!?Java类静态初始化里启线程,居然导致main函数阻塞!!!

先来看这个代码是咋写的 public class Lazy { public static boolean init = false; static { Thread t = new Thread(() -> init = true); t.start(); try { t.join(); ...

路漫漫 始于足下 846
上一篇: 多线程陷阱(不要调用run方法;静态的同步方法)
下一篇: 排序算法一(直接选择,堆排序,冒泡排序和快速排序)
方方
博客等级 码龄15年 6粉丝 7原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值