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

多线程中局部静态变量初始化的陷阱     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

传感器采集信息的频率(采样频率)

较高的采集频率通常能提供更详细的信息,但也会增加数据处理和存储的负担。在某些应用中,较高的采样频率可能是必要的,而在其他情况下,则可能会选择适当的低采样频率以降低功耗和数据处理压力。传感器采集信息的频率(采样频率)是指传感器在一定时间内对输入信号进行采样的次数。对于一些需要快速检测和响应的应用,如运动传感器、声音传感器等,高采样频率是至关重要的。高采样频率通常需要更多的功耗和更大的存储容量,因为产生的数据量更大。例如,对于快速运动的对象,较低的采样频率可能无法捕捉到运动的真实轨迹。

chen的博客 5341

LVGL仪表盘实战:5分钟用lv_meter打造汽车仪表UI(附完整代码)

本文详细介绍了如何使用LVGL的lv_meter组件在5分钟内构建专业汽车仪表盘UI。从环境配置、刻度定制到指针动画和性能优化,提供完整代码示例和实战技巧,帮助开发者快速实现嵌入式设备上的高效仪表盘设计。

k5l6m的博客 420

Java多线程中static变量的使用

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

weixin_30488085的博客 842

西门子S7-1200与FUNUC机器人MODBUS TCP通信

PLC与机器人通讯

Illusssion的博客 1万+

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

前期疑问:未完待续。

晴山の博客 956

操作系统实验一到实验九合集(哈工大李治军)

操作系统实验一到实验九合集(哈工大李治军) 有详细代码及注释,程序已在本地测试,均能跑通

leoabcd12的博客 5万+

SAP ABAP CO核心扩展-ZCKMLQS 多物料工厂的评估数量结构报表(CKMLQS)

CKMLQS 只能查看单一物料工厂的评估数量结构,SAP 没有提供多物料工厂的评估数量结构报表。这篇文章将实现 CO核心扩展-ZCKMLQS 多物料工厂的评估数量结构报表。

SAP李斌,本人承诺所有原创文章均不使用任何AI生成技术,请大家放心阅读。 1089

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

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

m0_37602227的博客 5697

Arcgis模型构建器01——写一个简单的模型

在使用模型构建器解决这个需求之前,我们要想清楚如果我们手动解决的话,应该如何去处理,因为构建模型的过程正是基于我们手动处理的过程来构建的。我有一个全国的dem影像数据和一个陕西省边界范围矢量,现在我想获取陕西省的dem影像的坡度数据。填写完之后点击确定,我们发现,两个输入数据和工具连接起来了,并且通过裁剪工具得到一个中间结果,即。我们发现工具框和中间数据框都存在阴影,说明已经运行过了这个模型,不报错就说明模型运行成功!现在我们就把数据和工具都串联起来了,成功构建好了模型。

爱吃雪糕的小布丁的博客 918

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

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

weixin_30519071的博客 89

【如何学习CAN总线测试】——Python+Robot Framework框架实现UDS诊断自动化

由于项目需要进行UDS诊断测试,所以对这方面进行了研究学习,网上很少能查询到相关资料,故记录一下UDS自动化测试开发过程,由于保密原则,案例都是Demo,希望能帮到感兴趣的朋友。

测试领域技术分享 7465

【YOLOv8全方位学习手册】第四章 数据增强:从扩充数据到塑造模型的认知边界

"""使用 Albumentations 做自定义增强,兼容 YOLO 格式"""])# 转成 numpy# boxes 必须是 [[x_min,y_min,x_max,y_max], ...]# 在数据加载时调用你可以把集成到 PyTorch 的 Dataset 类里,或使用 Ultralytics 自带的DataLoader。

VectorShift的博客 211

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

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

Never_Blue的博客 847

温度PID控制PLC程序

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

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

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

LiteTrans的博客 434

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

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

static。

qq_42896106的博客 627

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

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

本文深入探讨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、付费专栏及课程。

余额充值