Java 多线程是掌握高性能、高响应性应用程序开发的关键,它涉及到语言特性、JVM 实现、操作系统交互以及并发编程的核心概念。

核心目标: 充分利用现代多核 CPU 的计算能力,提高程序吞吐量(单位时间内处理的任务量)和响应性(避免用户界面卡死)。

一、 核心概念与基础

  1. 进程 vs. 线程:

    • 进程: 操作系统分配资源(内存、文件句柄、CPU 时间片)的基本单位。一个进程拥有独立的地址空间,进程间通信(IPC)成本较高(如管道、套接字)。
    • 线程: 轻量级进程 (Lightweight Process, LWP)。是进程内的一个独立执行流,共享其所属进程的内存空间(堆、方法区)和资源(文件句柄等),但拥有独立的程序计数器、虚拟机栈、本地方法栈和线程状态。线程间通信和数据共享成本远低于进程。一个 Java 程序(一个 JVM 进程)至少有一个主线程(main 方法所在线程)。
  2. Java 线程模型:

    • 1:1 模型 (内核级线程): 这是 Java 在主流操作系统(Windows, Linux, macOS)上默认采用的模型。每个 Java 线程直接映射到一个操作系统内核线程 (Kernel Thread)。由操作系统内核负责线程的调度和管理(CPU 时间片分配、上下文切换)。
      • 优点: 能真正利用多核 CPU 并行执行;阻塞操作(如 I/O)时,内核可以调度其他线程运行。
      • 缺点: 线程创建、销毁、上下文切换涉及系统调用,开销相对较大;受操作系统线程数限制。
    • 用户级线程 (历史/特定实现): 早期 Java 版本在某些平台上可能使用过用户级线程库(如 Green Threads)。线程管理完全在用户空间(JVM)进行,不依赖操作系统内核。创建/切换开销小,但一个线程阻塞会导致整个进程阻塞,且无法利用多核。现代 Java 已不再使用纯用户级线程模型。
    • M:N 模型 (混合线程 - Java 虚拟线程): Java 19 (Preview) / Java 21 (正式) 引入的 虚拟线程 (Virtual Threads) 采用此模型。多个虚拟线程 (M) 映射到少量操作系统线程 (N) 上执行。由 JVM 负责调度虚拟线程,在遇到阻塞操作时,JVM 能自动将虚拟线程挂起,并将底层承载线程 (Carrier Thread) 释放出来执行其他虚拟线程,避免阻塞 OS 线程。
      • 优点: 极大降低创建和管理高并发(成千上万)线程的开销;简化高吞吐量并发代码(特别是 I/O 密集型)。
      • 关系: 虚拟线程建立在强大的 java.util.concurrent 基础之上,是对传统平台线程 (java.lang.Thread) 的补充,而非替代。两者可以共存。
  3. 线程生命周期 (状态):
    Java 线程在其生命周期中会处于以下状态之一(定义在 Thread.State 枚举中):

    • NEW 线程对象已创建 (new Thread()),但尚未调用 start() 方法。
    • RUNNABLE 调用 start() 后进入此状态。注意: 这表示线程可以运行,但不一定正在 CPU 上执行。它可能在等待操作系统分配 CPU 时间片。包含了操作系统层面的 ReadyRunning 状态。
    • BLOCKED 线程试图获取一个由其他线程持有的对象监视器锁 (synchronized) 而进入阻塞状态。只有获得锁才能退出此状态
    • WAITING 线程等待另一个线程执行特定操作(通知或中断),无限期等待。进入方式:
      • Object.wait() (不指定超时)
      • Thread.join() (不指定超时)
      • LockSupport.park()
    • TIMED_WAITING 线程在指定时间段内等待另一个线程执行特定操作。进入方式:
      • Thread.sleep(long millis)
      • Object.wait(long timeout)
      • Thread.join(long millis)
      • LockSupport.parkNanos(long nanos) / LockSupport.parkUntil(long deadline)
    • TERMINATED 线程执行完 run() 方法或因异常退出。

二、 创建与启动线程

  1. 继承 Thread 类:

    class MyThread extends Thread {@Overridepublic void run() {// 线程要执行的代码}
    }
    MyThread thread = new MyThread();
    thread.start(); // 关键!调用 start() 让 JVM 安排执行 run(),不是直接调用 run()!
    
  2. 实现 Runnable 接口 (推荐):

    class MyRunnable implements Runnable {@Overridepublic void run() {// 线程要执行的代码}
    }
    Thread thread = new Thread(new MyRunnable());
    thread.start();
    // 或使用 Lambda 表达式简化
    Thread lambdaThread = new Thread(() -> {// 线程要执行的代码
    });
    lambdaThread.start();
    
    • 优势: 避免了单继承的限制;更符合面向对象设计(任务与执行者分离);便于线程池使用。
  3. 实现 Callable 接口 (带返回值):

    class MyCallable implements Callable {@Overridepublic String call() throws Exception {// 线程要执行的代码,可返回结果,可抛出异常return "Result";}
    }
    ExecutorService executor = Executors.newSingleThreadExecutor();
    Future future = executor.submit(new MyCallable());
    String result = future.get(); // 阻塞获取结果
    executor.shutdown();
    
    • 通常与 ExecutorService (线程池) 结合使用,通过 Future 获取异步计算结果。
  4. 虚拟线程 (Java 19+):

    // Java 21+
    Thread virtualThread = Thread.ofVirtual().start(() -> {// 线程要执行的代码 (I/O 密集型很合适)
    });
    // 或使用 Executors.newVirtualThreadPerTaskExecutor()
    
    • 创建开销极小,适合大量并发任务(特别是涉及阻塞 I/O 的)。

关键点: 必须调用 start() 方法来启动新线程。直接调用 run() 方法只是在当前线程中执行该方法,并没有创建新的执行流。

三、 线程同步与通信 - 核心挑战

多个线程共享进程内存空间,对共享数据的并发访问可能导致竞态条件 (Race Condition)数据不一致同步 (Synchronization) 是协调线程对共享资源访问的机制,确保线程安全 (Thread Safety)。

  1. 内置锁 (监视器锁) - synchronized 关键字:

    • 机制: 基于对象的内置锁 (Intrinsic Lock / Monitor Lock)。每个 Java 对象都有一个与之关联的锁。
    • 用法:
      • 同步代码块: synchronized (lockObject) { ... } - 显式指定锁对象。
      • 同步实例方法: public synchronized void method() { ... } - 锁是调用该方法的当前对象实例 (this)
      • 同步静态方法: public static synchronized void method() { ... } - 锁是该方法所属的 Class 对象
    • 原理:
      • 线程进入 synchronized 块/方法前,必须获得指定对象的锁。
      • 如果锁已被其他线程持有,当前线程进入 BLOCKED 状态等待。
      • 线程执行完 synchronized 块/方法后,会自动释放锁。
      • 锁是可重入 (Reentrant) 的:持有锁的线程可以再次获取同一个锁(避免自身死锁)。
    • 特点: 简单易用,JVM 内置支持。但粒度较粗(方法或代码块),容易导致死锁、性能下降(锁竞争)。
  2. volatile 关键字:

    • 目标: 解决内存可见性问题,不保证原子性
    • 可见性: 对一个 volatile 变量的写操作,会立即刷新到主内存。对一个 volatile 变量的读操作,会从主内存中读取最新值(绕过线程工作内存/缓存)。
    • 禁止指令重排序: JVM 和 CPU 会对指令进行优化重排序以提高性能。volatile 读写操作会插入内存屏障 (Memory Barrier),限制其前后指令的重排序,保证一定的顺序性。
    • 适用场景: 状态标志位(如 volatile boolean running;),double-checked locking 的单例模式实现(需要结合 synchronized)。
  3. java.util.concurrent 包 (JUC):
    提供了更强大、更灵活的并发工具,是构建高性能并发应用的首选。核心组件:

    • 锁 (Lock):
      • Lock 接口 (如 ReentrantLock): 提供比 synchronized 更灵活的锁操作(可中断、超时、尝试获取、公平锁/非公平锁选择)。
      • ReadWriteLock 接口 (如 ReentrantReadWriteLock): 允许多个读线程并发访问,但写线程独占访问(提高读多写少场景性能)。
    • 原子变量 (Atomic Variables):
      • AtomicInteger, AtomicLong, AtomicBoolean, AtomicReference 等。
      • 利用 CAS (Compare-And-Swap) 硬件指令(通过 sun.misc.Unsafe 或 JVM 内在函数)实现无锁(Lock-Free)的原子操作(如 incrementAndGet(), compareAndSet())。
      • 性能通常优于锁(在低到中度竞争下),避免上下文切换开销。
      • 是构建高性能非阻塞算法的基础。
    • 并发容器 (Concurrent Collections):
      • ConcurrentHashMap: 高并发、线程安全的 HashMap 实现(分段锁或 CAS)。
      • CopyOnWriteArrayList/CopyOnWriteArraySet: 写时复制,适合读多写少场景。
      • ConcurrentLinkedQueue/ConcurrentLinkedDeque: 无界、非阻塞、线程安全的队列(基于 CAS)。
      • BlockingQueue 接口及其实现 (ArrayBlockingQueue, LinkedBlockingQueue, PriorityBlockingQueue, SynchronousQueue, DelayQueue): 提供阻塞的 put()/take() 操作,是生产者-消费者模型的基石。
    • 线程池 (Thread Pools - ExecutorService):
      • 核心思想: 预先创建一组线程并管理其生命周期,通过任务队列接受并执行提交的任务 (Runnable/Callable)。
      • 优势:
        • 降低线程创建/销毁的开销。
        • 控制并发线程数量,避免资源耗尽。
        • 提供任务队列和多种拒绝策略。
        • 方便管理和监控。
      • 关键组件:
        • Executor / ExecutorService / ScheduledExecutorService 接口。
        • ThreadPoolExecutor: 最灵活、可配置的核心线程池实现。
        • Executors 工厂类:提供创建常用配置线程池的便捷方法(但需注意潜在问题,如 newFixedThreadPool 的无界队列可能导致 OOM)。
      • 重要配置参数: 核心线程数、最大线程数、任务队列、线程工厂、拒绝策略 (AbortPolicy, CallerRunsPolicy, DiscardPolicy, DiscardOldestPolicy)。
    • 同步工具 (Synchronizers):
      • CountDownLatch: 等待一组操作完成。初始化一个计数器,线程调用 countDown() 减 1,调用 await() 的线程阻塞直到计数器为 0。
      • CyclicBarrier: 让一组线程在某个公共屏障点等待,直到所有线程都到达屏障后才一起继续执行。可重用。
      • Semaphore: 控制访问特定资源的线程数量(许可证)。acquire() 获取许可,release() 释放许可。
      • Exchanger: 两个线程在同步点交换数据。
      • Phaser (Java 7+): 更灵活、可重用的同步屏障,支持动态注册/注销参与线程,分阶段同步。
    • Future 与 CompletableFuture:
      • Future: 表示异步计算的结果。提供检查是否完成、等待完成、获取结果的方法(阻塞)。
      • CompletableFuture (Java 8+): 强大的异步编程工具。支持显式完成、链式调用(thenApply, thenAccept, thenRun, thenCompose)、组合多个异步任务(thenCombine, allOf, anyOf)、异常处理(exceptionally, handle)。极大地简化了异步、非阻塞代码的编写。

四、 Java 内存模型 (JMM)

JMM 定义了线程如何以及何时可以看到其他线程写入共享变量的值,以及在必要时如何同步访问共享变量。它是理解 synchronized, volatile, finalhappens-before 关系的基础。

  1. 抽象模型:

    • 每个线程有自己的工作内存 (Working Memory)(可视为 CPU 寄存器、缓存等的抽象)。
    • 所有线程共享主内存 (Main Memory)(堆内存)。
    • 线程对变量的所有操作(读/写)都必须在工作内存中进行,不能直接读写主内存。
    • 线程间变量值的传递需要通过主内存来完成。
  2. 内存间交互操作:
    JMM 定义了 8 种原子操作(lock, unlock, read, load, use, assign, store, write)以及它们之间的顺序规则,但开发者主要关注其提供的可见性保证

  3. happens-before 原则:
    JMM 的核心是 happens-before 关系。它定义了两个操作之间的可见性保证:如果一个操作 A happens-before 操作 B,那么 A 所做的任何修改(写操作)对 B 都是可见的。

    • 程序顺序规则: 同一个线程中的每个操作,happens-before 于该线程中任意的后续操作。
    • 监视器锁规则: 对一个锁的解锁 (unlock),happens-before 于后续对这个锁的加锁 (lock)。
    • volatile 变量规则: 对一个 volatile 变量的写,happens-before 于任意后续对这个 volatile 变量的读。
    • 线程启动规则: Thread.start() 调用 happens-before 于被启动线程中的任何操作。
    • 线程终止规则: 线程中的所有操作 happens-before 于其他线程检测到该线程已经终止(通过 Thread.join() 返回或 Thread.isAlive() 返回 false)。
    • 中断规则: 一个线程调用另一个线程的 interrupt() happens-before 于被中断线程检测到中断(抛出 InterruptedException 或调用 isInterrupted()/interrupted())。
    • 传递性: 如果 A happens-before B,且 B happens-before C,那么 A happens-before C
  4. final 字段的特殊性:
    在对象构造器结束时,final 字段的值保证对其他线程可见(无需同步),前提是构造器没有将 this 引用逸出 (this escape)。

JMM 的意义: 它告诉开发者,在缺乏适当的同步(synchronized, volatile, JUC 工具)的情况下,一个线程对共享变量的修改,另一个线程不一定能立即、甚至永远看不到happens-before 规则是 JVM 必须遵守的契约,也是开发者编写正确并发程序的依据。

五、 高级主题与最佳实践

  1. 死锁 (Deadlock) 与活锁 (Livelock):

    • 死锁: 两个或多个线程互相持有对方需要的锁而无限期等待。必要条件:互斥、请求与保持、不可剥夺、循环等待。
    • 预防/避免: 固定加锁顺序、使用超时锁 (tryLock(timeout))、死锁检测算法。
    • 活锁: 线程持续响应对方动作(如反复重试)而无法取得进展。需要引入随机性或退避策略。
  2. 线程中断:

    • thread.interrupt():设置目标线程的中断标志位(非强制终止)。
    • thread.isInterrupted():检查线程是否被中断(不清除标志)。
    • Thread.interrupted():检查当前线程是否被中断,并清除中断标志。
    • 阻塞方法(如 sleep(), wait(), join())在阻塞时收到中断信号会抛出 InterruptedException(抛出前会清除中断标志)。正确处理中断是编写健壮多线程代码的关键(通常选择传递 InterruptedException 或恢复中断状态)。
  3. ThreadLocal

    • 为每个线程创建变量的独立副本,解决共享变量冲突问题。
    • 常用于存储线程上下文信息(如用户会话 ID、数据库连接),避免在方法间传递参数。
    • 注意内存泄漏: ThreadLocal 变量通常作为 static final 字段声明。线程池中的线程可能长期存活,如果 ThreadLocal 值引用了大对象且不再使用,需要手动调用 remove() 清除,否则该对象无法被 GC。
  4. 性能考量与调优:

    • 减少锁竞争: 缩小同步范围、使用读写锁、使用并发容器、使用原子变量、考虑无锁数据结构。
    • 合理使用线程池: 根据任务类型(CPU 密集型 vs I/O 密集型)设置核心/最大线程数、选择合适队列和拒绝策略。避免使用无界队列(可能导致 OOM)和 Executors.newCachedThreadPool()(可能导致创建过多线程),推荐手动创建 ThreadPoolExecutor
    • 利用虚拟线程 (Java 21+): 对于高并发、大量阻塞操作(尤其是 I/O)的任务,虚拟线程能显著提升吞吐量和资源利用率。
    • 监控: 使用 JConsole, VisualVM, Java Mission Control 等工具监控线程状态、死锁、CPU 使用率。
  5. 结构化并发 (Java 21+ - Preview):

    • 旨在简化并发任务的生命周期管理,特别是处理任务组及其子任务。
    • 核心思想:子任务的生命周期应限定在其父任务的语法块内。使用 StructuredTaskScope
    • 优势:提高代码可读性、可靠性和可维护性;自动处理取消和错误传播;避免线程泄漏。

总结

Java 多线程机制是一个庞大而复杂的主题,其核心在于利用硬件并行能力安全高效地协调并发访问共享资源。深入理解需要掌握:

  1. 线程模型与生命周期: 理解线程如何创建、执行、阻塞和终止。
  2. 同步原语: 从基础的 synchronizedvolatile 到强大的 JUC 工具(锁、原子类、并发容器、线程池、同步器),理解它们的原理、适用场景和优缺点。
  3. Java 内存模型 (JMM) 与 happens-before 这是理解内存可见性、指令重排序和编写正确并发程序的理论基石。
  4. 高级问题处理: 死锁/活锁的识别与避免、正确的中断处理、ThreadLocal 的合理使用与风险。
  5. 现代趋势: 虚拟线程 (Virtual Threads) 极大地简化了高吞吐量 I/O 密集型并发编程;结构化并发 (Structured Concurrency) 提升了并发代码的可管理性和可靠性。

最佳实践的核心永远是:优先使用高级并发工具 (JUC),清晰理解共享状态,最小化同步范围,合理利用线程池,并时刻关注线程安全和性能。 随着 Java 的演进(特别是虚拟线程和结构化并发),编写高效、可维护的并发代码将变得更加容易和安全。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android热修复实现方案深度分析

热修复的核心目标是在**不发布新版本、不重新安装、不重启应用(或仅轻量级重启)**的情况下,修复线上应用的 Bug 或进行小范围的功能更新,极大地提升用户体验和问题响应速度。 一、热修复的核心原理 无论哪种方案,其核心…

HTML前端颜色渐变动画完整指南

渐变动画已经成为现代网页设计中不可或缺的元素,它们不仅能为网站增添视觉吸引力,还能显著提升用户体验。通过巧妙运用CSS渐变动画,开发者可以创造出令人印象深刻的动态背景效果,而无需依赖图片或复杂的脚本。 渐变动画的魅力所在…

b-up:Enzo_mi:Transformer DETR系列

1.视频1:self-Attention|自注意力机制 |位置编码 | 理论 代码 注意: q-查询; k-商品标签; v-值(具体商品) * 不是指乘法,类似概念 a1:相似度; b1:总分 若想…

算法题(179):单调栈

审题: 本题是单调栈的模板题 补充:单调栈 单调栈中的数据始终保持单调递增或单调递减 使用情景:给定一个数组,要求寻找 1.某个数左侧,离他最近且值大于他的数 2.某个数左侧,离他最近且值小于他的数 3.某个数…

CF每日5题(1500-1600)

545C 贪心 1500 题意:给 n 棵树在一维数轴上的坐标 xix_ixi​ ,以及它们的长度 hih_ihi​。现在要你砍倒这些树,树可以向左倒也可以向右倒,砍倒的树不能重合、当然也不能覆盖其他的树原来的位置,现在求最大可以砍倒的…

HW蓝队:天眼告警监测分析之Web攻击

Web攻击 信息泄露 敏感数据包括但不限于:口令、密钥、证书、会话标识、License、隐私数据(如短消息的内容)、授权凭据、个人数据(如姓名、住址、电话等)等,在程序文件、配置文件、日志文件、备份文件及数据库中都有可能包含敏感数据 信息收集方法 漏洞分类 备份文…

大腾智能国产3D CAD软件正式上架华为云云商店

深圳市大腾信息技术有限公司(以下简称“大腾智能”)与华为云达成深度合作,大腾智能CAD软件及配套服务通过了华为云在功能适配、安全可用、稳定高效等方面的严选商品认证,已正式上架华为云云商店,成为华为云云商店的联营…

论文复现-windows电脑在pycharm中运行.sh文件

1.更改终端路径(前提:已下载git bash)2.授权打开pycharm终端,输入 chmod x 文件名3.根据当前位置,运行.sh文件

开关电源安全保护电路:浪涌保护、过流保护、过压保护

开关电源安全保护电路:浪涌保护、过流保护、过压保护 引言 对于开关电源而言, 安全、可靠性历来被视为重要的性能之一. 开关电源在电气技术指标满足电子设备正常使用要求的条件下, 还要满足外界或自身电路或负载电路出现故障的情况下也能安全可靠地工作. 为此, 须有多种保护措…

C语言(十)

一、函数概述函数是面向过程编程思想的具体体现,主要作用:降低程序之间的耦合性提高代码的复用性和可维护性一个完整的 C 程序由**一个或多个程序模块(源文件)**组成。为便于开发与调试,通常会将代码拆分为多个源文件&…

QT项目-仿QQ音乐的音乐播放器(第二节)

目录 自定义控件: BtForm类中实现 BtForm上的动画效果 自定义控件: 该控件实际由:图⽚、⽂字、动画三部分组成。图⽚和⽂字分别⽤QLabel展⽰,动画部分内部实际为4 个QLabel。 ① 将BtForm的geometry的宽度和⾼度修改为200*35。…

【世纪龙科技】数字课程资源-新能源汽车概论

一、课程介绍本课程为通过项目任务式教学,全面系统的讲解了新能源汽车的基础知识及相关技能,培养和提高学生的动手能力和理论知识的工程应用能力。以典型工作任务带动知识与技能的学习,采用项目教学培养学生的岗位技能、学习能力和职业素养。…

iOS Core Data 本地数据库 使用详解:从模型关系到数据操作

一、引言:Core Data,在本地数据持久化中的地位在 iOS 开发中,本地数据存储几乎是每一个 App 都绕不开的问题。无论是缓存用户信息、离线浏览内容,还是记录用户操作历史,一个合适的数据持久化方案都能大大提升应用的体验…

Java-79 深入浅出 RPC Dubbo 动态路由架构详解:从规则设计到上线系统集成

点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) AI炼丹日志-30-新发布【1T 万亿】参数量大模型!Kim…

Linux内核中动态内存分配函数解析

在C语言中,动态内存分配通常用于在运行时申请内存。在内核编程中,动态内存分配与用户空间有所不同,因为内核需要更谨慎地处理内存,且不能使用用户空间的库(如glibc)。下面我们将详细分析Linux内核中动态申请…

Next.js 中配置不同页面布局方案

在 Next.js 应用中,你可以通过多种方式实现某些页面全屏、某些页面带菜单/页眉/页脚的需求。以下是几种实现方案: 方案一:使用多个布局组件 1. 创建不同的布局组件 // app/default-layout.tsx import Header from /components/header; import…

Spring Boot 使用外置 Servlet 容器:从配置到部署全指南

在 Spring Boot 开发中,我们通常使用嵌入式 Servlet 容器(如 Tomcat),它能将应用打包成可执行 JAR,简化部署流程。但在某些场景下(如需要支持 JSP、复杂的容器定制或企业级部署规范)&#xff0c…

借助AI学习开源代码git0.7之九diff-files

借助AI学习开源代码git0.7之九diff-files diff-files.c 是一个用于比较工作目录中的文件和 Git 索引(暂存区)中文件的工具。 实质上,它是 git diff命令在不指定特定提交时功能的核心实现。 主要功能分析: 1. 核心功能 diff-files …

社区资源媒体管理系统设计与实现

社区资源媒体管理系统设计与实现 1. 系统概述 社区资源媒体管理系统是一个专为社区户外广告打造的高效、专业化平台,旨在实现社区媒体的数字化管理、智能投放和便捷交易。该系统将整合社区各类广告资源,为广告主、物业公司和社区居民提供一站式服务。 1.…

12.1.6 weak_ptr

weak_ptr weak_ptr会指向一个share_ptr&#xff08;使用一个share_ptr来初始化weak_ptr&#xff09;&#xff0c;但并不会增加这个share_ptr的引用计数器&#xff0c;其析构也不会减少share_ptr的引用计数器。 构造函数及使用 #include <iostream> #include <memory&g…