一文掌握:Java中防止对象溢出与不安全发布

1. 对象发布与安全发布的概念

1.1 对象发布的定义

在Java并发编程中,对象发布(Object Publishing)是一个中心概念。它涉及到一个对象从创建者的手中传递到其他线程的过程中的安全性问题。一般而言,当一个对象被初始化后,它可以被发布到其他线程中去使用,通过这个过程,其他线程可能会对这个对象进行读取或修改。

1.2 什么是安全发布(Safe Publication)

安全发布确保对象在发布到其他线程时,状态的正确性和可见性得到保障。换言之,当一个对象被安全发布时,任何线程都能看到这个对象的最新状态且不会遇到过期数据的问题。安全发布的方法包括但不限于使用final关键字、volatile关键字、锁(synchronized block)或者原子引用。

1.3 不安全发布的危害

不安全地发布一个对象可能导致严重的并发缺陷,比如线程看到的对象状态不一致(可视性问题)、出现意料之外的异常、甚至导致程序崩溃。这些问题通常源自于Java内存模型的特性,导致未能正确地同步对象状态。

2. 不安全的发布实例分析

2.1 不安全的发布示例代码

public class UnsafePublish {
    private String instance;

    public UnsafePublish() {
        this.instance = "Initial Value";
    }

    public void publish() {
        // 假设这是一种不安全发布的方式
        SharedObject.sharedInstance = this.instance;
    }
}

在这段代码中,instance变量被发布到了共享对象SharedObject.sharedInstance中,但此发布方式未确保其他线程对instance变量值的可见性。这可能导致其他线程看到的instance变量为null或者旧的值。

2.2 对象溢出示例代码

public class ThisEscape {
    public ThisEscape(EventSource source) {
        source.registerListener(new EventListener() {
            public void onEvent(Event e) {
                // ThisEscape.this 未能被安全发布
                doSomething(e);
            }
        });
    }
    // 省略其他方法和属性
}

这是一个经典的“this逸出”(This Escape)例子,在对象构造还未完成时,this引用已经逸出。当事件监听器试图访问对象的状态时,可能访问到尚未初始化完成的属性。

3. 线程安全与单例模式深入理解

3.1 懒汉 vs. 饿汉模式

单例模式确保了一个类仅有一个实例,并提供一个全局的访问点。在实现单例时,懒汉模式和饿汉模式是两种常见的实现方式。懒汉模式延迟实例的创建直至第一次使用,而饿汉模式则在类加载时就创建实例。

3.2 单例模式的线程不安全性分析

单例模式在多线程环境下的线程安全性问题一直是一个讨论的焦点。对于懒汉模式,如果多个线程同时请求实例,可能会创建多个实例。对于饿汉模式,由于实例在类加载时就创建,通常被认为是线程安全的;但如果存在复杂的依赖关系或执行长时间的初始化操作,也可能导致问题。

4. 不同单例模式实现的对比及实战代码

4.1 线程不安全的懒汉模式实现

public class SingletonLazy {
    private static SingletonLazy instance;
    
    private SingletonLazy() {}

    public static SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy(); // 线程不安全
        }
        return instance;
    }
}

在这段代码中,如果多个线程同时进入if检查,都发现instance为null,那么它们都会创建一个新的实例,违反了单例模式的原则。

4.2 线程安全的饿汉模式实现

public class SingletonEager {
    private static final SingletonEager instance = new SingletonEager();
    
    private SingletonEager() {}

    public static SingletonEager getInstance() {
        return instance;
    }
}

饿汉模式在类加载时就完成了实例的初始化,确保了实例的唯一性和线程安全。

4.3 非推荐的线程安全懒汉模式实现

public class SingletonLazySyncMethod {
    private static SingletonLazySyncMethod instance;
    
    private SingletonLazySyncMethod() {}

    public static synchronized SingletonLazySyncMethod getInstance() {
        if (instance == null) {
            instance = new SingletonLazySyncMethod(); // 线程安全,但效率低下
        }
        return instance;
    }
}

虽然用synchronized方法确保了线程安全,但是每次调用这个方法时,必须获取对象的锁,这在高并发环境下会显著降低效率。

4.4 双重锁同步锁单例模式的问题点

public class SingletonDoubleChecked {
    private static SingletonDoubleChecked instance;

    private SingletonDoubleChecked() {}

    public static SingletonDoubleChecked getInstance() {
        if (instance == null) {
            synchronized (SingletonDoubleChecked.class) {
                if (instance == null) {
                    instance = new SingletonDoubleChecked(); // 可能的线程安全问题
                }
            }
        }
        return instance;
    }
}

双重检查加锁首先检查实例是否已创建,如果未创建,才进行同步。这种方式可能因为指令重排导致线程安全问题。

4.5 通过volatile与双重检测实现线程安全的懒汉模式

public class SingletonLazyDoubleChecked {
    private static volatile SingletonLazyDoubleChecked instance;
    
    private SingletonLazyDoubleChecked() {}

    public static SingletonLazyDoubleChecked getInstance() {
        if (instance == null) {
            synchronized (SingletonLazyDoubleChecked.class) {
                if (instance == null) {
                    instance = new SingletonLazyDoubleChecked(); // volatile 阻止了指令重排,确保线程安全
                }
            }
        }
        return instance;
    }
}

使用volatile关键字可以禁止指令重排,保证在写操作未完成之前,不会读取到该变量,这样就可以安全地实现双重检查锁定。

4.6 使用静态代码块实现的饿汉模式

public class SingletonStaticBlock {
    private static final SingletonStaticBlock instance;
    
    static {
        instance = new SingletonStaticBlock();
    }

    private SingletonStaticBlock() {}

    public static SingletonStaticBlock getInstance() {
        return instance;
    }
}

使用静态代码块的方式对于使用复杂的实例创建逻辑或是有异常处理需求的情况是一种不错的选择。

4.7 枚举方式实现单例的优势分析

public enum SingletonEnum {
    INSTANCE;

    public void doSomething() {
        // 执行操作
    }
}

枚举实现单例模式是最简洁、自然且安全的方式。由Java虚拟机保证了每个枚举常量的唯一性和单一实例。枚举方式也自然地支持序列化机制,无需担心序列化创建新的实例。

5. Java内存模型与安全发布

5.1 JMM基础概念回顾

Java 内存模型(Java Memory Model, JMM)是一种规范,用来屏蔽各种硬件和操作系统的内存访问差异,确保在多线程情况下对共享变量的访问能够正确处理。JMM 关于并发的核心概念主要包括原子性、可见性和有序性。对于安全发布来说,可见性和有序性是核心关注的点。

5.2 通过JMM解析volatile关键字的作用

volatile关键字是一种轻量级的同步策略,它保证了被其修饰的变量对所有线程的可见性。当一个共享变量被volatile修饰后,确保了对这个变量的写操作先于后续对这个变量的读操作。这也意味着,对于volatile变量的修改,线程每次都是从主存中读取和写入,而不会缓存该变量。

5.3 happens-before规则与安全发布

Java 提供了"happens-before"规则来确保内存操作的有序性。happens-before 原则定义了一个全局的顺序,它为程序中所有的并发操作提供了一个定序关系。在安全发布的上下文中,重要的happens-before 规则包括:

  • 对象的构造函数执行结束happens-before 它的finalize()方法。
  • 一个线程中的所有操作happens-before 其他线程在该线程上成功返回的join()方法的调用。
  • 对volatile变量的写操作happens-before 后续对这个变量的读操作。

6. 安全发布最佳实践与案例

6.1 实践:如何进行安全发布

要安全地发布一个对象,可以使用以下几种方式:

  • 使用final关键字修饰对象的引用,确保对象地址不可变且对象状态在构造后不可变。
  • 使用锁(synchronized块或ReentrantLock)来控制对象的访问。
  • 使用原子引用类如AtomicReference来发布对象。
  • 使用正确构造的volatile变量或通过volatile读写同步变量。

6.2 案例分析:在实际项目中安全地发布对象

考虑如下场景:一个线程需要将配置信息发布给多个消费者线程。为了保证配置信息的安全发布,可以采取以下方法:

public class SafePublish {
    
    private volatile Config config;

    public void updateConfig(NewConfig newConfig) {
        Config oldConfig = this.config;
        this.config = new Config(newConfig); // 用 volatile 保证配置更新对其他线程立即可见
    }

    public Config getConfig() {
        return config; // 直接访问 volatile 字段
    }
}

public class Config {
    // Configuration details, constructors, getters etc.
}

这里将config声明为volatile,保证了新的配置信息能够被立即发布和对所有消费者线程可见。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

my-room-in-3d中的电脑,电视,桌面光带发光原理

1. my-room-in-3d中的电脑,电视,桌面光带发光原理 最近在github中,看到了这样的一个项目; 项目地址 我看到的时候,蛮好奇他这个光带时怎么做的。 最后发现,他是通过,加载一个 lightMap.jpg这个…

大型语言模型的新挑战:AMR语义表示的神秘力量

DeepVisionary 每日深度学习前沿科技推送&顶会论文&数学建模与科技信息前沿资讯分享,与你一起了解前沿科技知识! 引言:AMR在大型语言模型中的作用 在自然语言处理(NLP)的领域中,抽象意义表示&…

查找算法与排序算法

查找算法 二分查找 (要求熟练) // C// 二分查找法(递归实现) int binarySearch(int *nums, int target, int left, int right) // left代表左边界,right代表右边界 {if (left > right) return -1; // 如果左边大于右边,那么…

esp8266与uno使用软串口通信

esp8266的d6和d5分别与uno的5和6管脚连接&#xff1a; uno程序&#xff1a; //uno #include <SoftwareSerial.h> SoftwareSerial s(5,6);//(RX,TX)void setup(){s.begin(9600);Serial.begin(9600); }void loop(){int data50;if (s.available() > 0) {char c s.read(…

【错题集-编程题】比那名居的桃子(滑动窗口 / 前缀和)

牛客对应题目链接&#xff1a;比那名居的桃子 (nowcoder.com) 一、分析题目 1、滑动窗口 由题意得&#xff0c;我们是要枚举所有大小为 k 的子数组&#xff0c;并且求出这段⼦数组中快乐值和羞耻度之和。因此&#xff0c;可以利用滑动窗口的思想&#xff0c;用两个变量维护大小…

【区块链】共识算法简介

共识算法简介 区块链三要素&#xff1a; 去中心化共识算法智能合约 共识算法作为区块链三大核心技术之一&#xff0c;其重要性不言而喻。今天就来简单介绍共识算法的基本知识。 最简单的解释&#xff0c;共识算法就是要让所有节点达成共识&#xff0c;保证少数服从多数&#x…

从零开始学AI绘画,万字Stable Diffusion终极教程(六)

【第6期】知识补充 欢迎来到SD的终极教程&#xff0c;这是我们的第六节课&#xff0c;也是最后一节课 这套课程分为六节课&#xff0c;会系统性的介绍sd的全部功能&#xff0c;让你打下坚实牢靠的基础 1.SD入门 2.关键词 3.Lora模型 4.图生图 5.controlnet 6.知识补充 …

初识C语言——第九天

ASCII定义 在 C 语言中&#xff0c;每个字符都对应一个 ASCII 码。ASCII 码是一个字符集&#xff0c;它定义了许多常用的字符对应的数字编码。这些编码可以表示为整数&#xff0c;也可以表示为字符类型。在 C 语言中&#xff0c;字符类型被定义为一个整数类型&#xff0c;它占…

C/C++开发,opencv-ml库学习,K近邻(KNN)应用

目录 一、k近邻算法 1.1 算法简介 1.2 opencv-k近邻算法 二、cv::ml::KNearest应用 2.1 数据集样本准备 2.2 KNearest应用 2.3 程序编译 2.4 main.cpp全代码 一、k近邻算法 1.1 算法简介 K近邻算法&#xff08;K-Nearest Neighbor&#xff0c;KNN&#xff09;基本原理是…

Vue按照顺序实现多级弹窗(附Demo)

目录 前言1. 单个弹窗2. 多级弹窗 前言 强化各个知识点&#xff0c;以实战融合&#xff0c;以下两个Demo从实战提取 1. 单个弹窗 部署按钮框以及确定的方法即可 截图如下所示&#xff1a; 以下Demo整体逻辑如下&#xff1a; 点击“生成周月计划”按钮会触发showWeekPlanDia…

FLIR LEPTON3.5 热像仪wifi 科研实验测温采集仪

点击查看详情!点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情 1、描述 这是一款桌面科研实验测温热成像多功能热像记录仪&#xff0c;小巧轻便…

STM32微秒级别延时--F407--TIM1

基本配置&#xff1a; TIM1挂载在APB2总线上&#xff0c;150MHz经过15分频&#xff0c;得到10MHz计数频率&#xff0c;由于disable了自动重装载&#xff0c;所以只需要看下一次计数值是多少即可。 void TIM1_Delay_us(uint16_t us) //使用阻塞方式进行延时&#xff0c;ARR值不…

记录vue报错问题 in ./node_modules/axios/lib/platform/index.js

今天这个问题困扰了我许久 报错内容如下&#xff1a; 最初一直以为是我没装axios&#xff0c;又重新装了一次&#xff0c;后面才发现是axios版本原因&#xff0c;真的总是被版本的原因困住真的很烦 解决方法如下&#xff1a; 将axios的版本改为1.5.0 1、打开项目的文件夹“…

Linux命令--查找占磁盘空间最大的文件

原文网址&#xff1a;Linux命令--查找占磁盘空间最大的文件-CSDN博客 简介 本文介绍Linux怎样查找占磁盘空间最大的文件。 1.找到占空间最大的分区 命令 df -h 结果 2.查找分区里最大的文件 法1&#xff1a;直接查找最大的文件 sudo find my_folder -type f -exec du -…

LangChain-RAG学习之 LangChain框架入门

什么是LangChain LangChain是一个强大的框架&#xff0c;旨在帮助开发人员使用语言模型构建端到端的应用程序。它提供了一套工具、组件和接口&#xff0c;可简化创建由大型语言模型 (LLM) 和聊天模型提供支持的应用程序的过程。LangChain 可以轻松管理与语言模型的交互&#x…

使用FastGPT+OneAPI在本地使用Llama3

FastGPT 是一个基于 LLM 大语言模型的知识库问答系统&#xff0c;提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排&#xff0c;从而实现复杂的问答场景&#xff01;他的重要特点就是工作流编排。 工作流编排&#xff1a;基于 Flow 模块的工作…

OneNote导出白色背景文件时将笔记墨迹转换颜色

今天用OneNote导出笔记时发现在文件上做的黑色墨迹笔记全部转成了白色。推测是因为onenote会根据背景色自动转换黑色和白色的墨迹&#xff0c;但是其他颜色好像导出的时候不会转换。 于是&#xff0c;我们首先要转换背景&#xff0c;将黑色背景转成白色背景&#xff0c; 然后将…

国内各种免费AI聊天机器人(ChatGPT)推荐(中)

作者主页&#xff1a;点击&#xff01; 国内免费AI推荐(ChatGPT)专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年4月29日15点20分 随着人工智能技术的不断发展&#xff0c;AI聊天机器人已经逐渐融入我们的日常生活。它们可以提供各种服务&#xff0c;例如聊天、…

【数据结构】链表专题2

前言 本篇博客继续探讨有关链表的专题&#xff0c;这片博客的题&#xff0c;提前打个预防针&#xff0c;有点意思哦&#xff0c;哈哈哈&#xff0c;话不多说&#xff0c;进入正文 &#x1f493; 个人主页&#xff1a;小张同学zkf ⏩ 文章专栏&#xff1a;数据结构 若有问题 评论…

【C语言】分支和循环(上)

【C语言】分支和循环&#xff08;上&#xff09; 1、if语句1.2 else1.3分支中包含多条语句1.4嵌套if1.5悬空else问题 2、关系操作符3、条件操作符4、逻辑操作符&#xff1a;与、或、非&#xff08;取反&#xff09;&#xff08;&&&#xff0c;||&#xff0c;&#xff0…
最新文章