news 2026/4/3 6:33:30

JavaEE——多线程(4)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaEE——多线程(4)

1.线程安全问题

好的,我们来详细探讨一下线程安全问题的原因,并以 Java 为例进行说明。

线程安全问题是指当多个线程同时访问共享的可变资源(如变量、对象、文件、数据库连接等)时,由于执行顺序的不确定性,可能导致程序的行为出现不符合预期的错误结果。这种问题在多线程编程中非常常见且难以调试。

在 Java 中,线程安全问题主要源于以下几个方面:

  1. 共享数据的可见性问题

    • 原因:Java 内存模型允许线程拥有自己的工作内存(通常是 CPU 寄存器或缓存),线程对共享变量的操作首先发生在工作内存中,之后才会同步到主内存。不同线程的工作内存可能持有共享变量的不同副本。
    • 后果:一个线程对共享变量所做的修改,可能不会立即(甚至永远不)对其他线程可见。例如,线程 A 修改了共享变量counter的值,但线程 B 可能在一段时间内(甚至永远)看到的仍然是修改前的旧值。
    • Java 特性:volatile关键字可以部分解决可见性问题,确保对该变量的读写都直接发生在主内存中,从而保证修改对所有线程立即可见。
  2. 操作的原子性问题

    • 原因:原子性是指一个操作作为一个不可分割的整体执行,要么完全执行成功,要么完全不执行,不会出现执行到一半的状态。然而,许多看似简单的操作在底层可能由多个步骤组成。
    • 后果:当多个线程同时执行一个非原子操作时,它们的执行步骤可能会相互交错。例如,常见的i++操作在 Java 中并非原子操作,它实际上包含三个步骤:
      1. 读取i的当前值。
      2. 将值加 1。
      3. 将新值写回i。 如果两个线程同时执行i++(假设初始i=0),可能的执行顺序是:线程1读(0) -> 线程2读(0) -> 线程1写(1) -> 线程2写(1)。最终i的结果是 1,而不是预期的 2。
    • Java 特性:Java 中的基本类型(除longdouble外)的读写操作本身是原子的。但像i++这样的复合操作是非原子的。要保证复合操作的原子性,需要使用同步机制,如synchronized块/方法或java.util.concurrent.atomic包中的原子类(如AtomicInteger)。
  3. 代码执行顺序问题(重排序)

    • 原因:为了提高性能,编译器和处理器(CPU)可能会对指令的执行顺序进行重新排序。这种重排序在不改变单线程程序执行结果的前提下是允许的。然而,在多线程环境下,这种重排序可能导致意想不到的结果。
    • 后果:线程 A 和 B 看到的代码执行顺序可能与源代码顺序不同。例如:
      // 线程 A flag = true; // 语句1 x = 42; // 语句2
      编译器或处理器可能会先执行语句2再执行语句1(重排序)。如果线程 B 看到flag == true后去读取x,它可能错误地认为x已经被赋值为 42,但实际上x可能还是初始值(0)或未定义的值。
    • Java 特性:Java 内存模型定义了happens-before规则,规定了哪些操作必须保证在另一些操作之前发生(即保证可见性和顺序)。使用volatilesynchronizedfinal关键字以及某些并发工具类(如CountDownLatch)可以建立happens-before关系,防止有害的重排序影响程序逻辑。

总结核心问题:

线程安全问题的核心在于多个线程对共享的、可变的状态(数据)进行非同步的并发访问。共享性(多个线程都能访问)、可变性(数据可以被修改)和并发性(访问同时或交错发生)是导致线程安全问题的三个必要条件。缺少其中任何一个,通常就不会有线程安全问题(例如,只读的共享数据通常是线程安全的)。

简单示例说明:

public class UnsafeCounter { private int count = 0; // 共享可变状态 public void increment() { // 非原子操作 count++; // 包含读-改-写三个步骤 } public int getCount() { return count; } }

如果多个线程同时调用同一个UnsafeCounter实例的increment()方法,最终getCount()返回的值很可能小于实际调用的总次数(因为发生了丢失更新)。这就是典型的由于操作非原子性(以及可能的可见性问题)导致的线程安全问题。

要解决线程安全问题,就需要通过锁(如synchronized)、原子变量、不可变对象、线程局部存储(ThreadLocal)或并发容器等机制,来确保对共享状态的操作是同步的、原子的,或者从根本上避免共享可变状态。

2.单列模式

好的,我们来探讨Java中的单例模式

单例模式是一种创建型设计模式,其核心目标是确保一个类在整个应用程序的生命周期中仅有一个实例存在,并且提供一个全局访问点来获取这个实例。这在需要控制资源(如数据库连接池、线程池)或确保某些对象全局唯一时非常有用。

实现单例模式的关键点

  1. 私有化构造方法:防止外部通过new关键字直接创建实例。
  2. 持有自身类型的静态私有成员:用于保存那个唯一的实例。
  3. 提供静态公有方法:作为全局访问点,用于获取该唯一实例。

线程安全的实现(双重校验锁)

这是最常用且推荐的一种实现方式,兼顾了线程安全、延迟初始化和效率。

public class Singleton { // 私有静态成员,用于持有唯一实例。volatile确保可见性和防止指令重排序 private static volatile Singleton instance; // 私有构造方法,防止外部实例化 private Singleton() { // 初始化逻辑(可选) } // 公有静态方法,全局访问点 public static Singleton getInstance() { // 第一次检查:避免不必要的同步开销(如果实例已经存在) if (instance == null) { // 同步块,保证线程安全 synchronized (Singleton.class) { // 第二次检查:防止在等待锁期间其他线程已经创建了实例 if (instance == null) { instance = new Singleton(); } } } return instance; } // 其他业务方法 public void doSomething() { // ... } }

说明:

  1. volatile关键字
    • 确保多线程环境下,线程能够看到instance变量的最新值。
    • 防止指令重排序,避免线程看到一个未完全初始化的对象(半初始化状态)。
  2. 双重检查
    • 第一次检查(if (instance == null))避免了大多数情况下的同步开销,提高了性能。
    • 进入synchronized块后,再次检查(if (instance == null))是为了防止多个线程在第一次检查都通过后,依次进入同步块创建多个实例。
  3. 延迟初始化:实例在第一次调用getInstance()时才被创建。

其他实现方式(简要提及)

  • 饿汉式
    public class Singleton { private static final Singleton instance = new Singleton(); private Singleton() {} public static Singleton getInstance() { return instance; } }
    • 优点:简单、线程安全(利用类加载机制)。
    • 缺点:无论是否使用,实例都会被创建,可能造成资源浪费。
  • 静态内部类(Holder模式)
    public class Singleton { private Singleton() {} private static class Holder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return Holder.INSTANCE; } }
    • 优点:线程安全、延迟加载(在调用getInstance()时加载Holder类并创建实例)。
    • 缺点:无法通过参数化构造器初始化。

注意事项

  • 序列化:如果单例类实现了Serializable接口,反序列化可能会创建新实例。可以通过实现readResolve()方法返回现有实例来解决:
    private Object readResolve() { return getInstance(); }
  • 反射攻击:可以通过反射调用私有构造方法创建新实例。可以通过在构造方法中检查instance是否已存在来防御(但非绝对安全)。
  • 过度使用:单例模式可能导致代码耦合度高、难以测试(全局状态),应谨慎使用。考虑依赖注入等方式管理共享资源。

双重校验锁(带volatile)是Java中实现线程安全单例的常用且可靠的方法。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/3 3:59:33

使用Docker镜像源加速GLM-4.6V-Flash-WEB部署全过程

使用Docker镜像源加速GLM-4.6V-Flash-WEB部署全过程 在多模态AI应用日益普及的今天,如何快速、稳定地将视觉语言模型(VLM)部署到生产环境,已成为开发者面临的核心挑战之一。尤其是在国内网络环境下,直接拉取海外Docke…

作者头像 李华
网站建设 2026/3/29 22:21:48

ComfyUI自定义组件封装GLM-4.6V-Flash-WEB调用逻辑

ComfyUI自定义组件封装GLM-4.6V-Flash-WEB调用逻辑 在如今多模态AI应用爆发式增长的背景下,图文理解、视觉问答和内容生成等任务早已不再是实验室里的概念,而是真实落地于智能客服、教育辅助、内容审核乃至创意设计中的核心能力。然而,对于大…

作者头像 李华
网站建设 2026/4/2 0:23:24

Git commit规范助力GLM-4.6V-Flash-WEB项目协作开发

Git Commit 规范如何赋能 GLM-4.6V-Flash-WEB 的高效协作 在开源 AI 项目日益复杂的今天,一个模型能否被广泛采用,往往不只取决于它的推理精度或响应速度,更在于其背后的工程成熟度。以智谱推出的 GLM-4.6V-Flash-WEB 为例,这款面…

作者头像 李华
网站建设 2026/3/27 10:54:49

JavaScript节流控制GLM-4.6V-Flash-WEB请求频率

JavaScript节流控制GLM-4.6V-Flash-WEB请求频率 在构建现代Web端AI应用时,一个常被低估却至关重要的问题浮出水面:用户操作的随意性与模型服务资源有限性之间的矛盾。以智谱AI推出的 GLM-4.6V-Flash-WEB 为例,这款专为高并发、低延迟设计的轻…

作者头像 李华
网站建设 2026/4/2 4:50:08

强烈安利8个AI论文软件,专科生轻松搞定毕业论文!

强烈安利8个AI论文软件,专科生轻松搞定毕业论文! AI 工具,正在改变论文写作的规则 对于专科生来说,毕业论文往往是一道难以逾越的门槛。从选题到撰写,再到查重降重,每一个环节都可能让人感到焦虑和无助。而…

作者头像 李华