news 2026/5/9 15:18:03

一文搞懂 Java 并发编程【附示例代码】

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
一文搞懂 Java 并发编程【附示例代码】

我是十几年Java资深开发,专注架构/面试,关注我,持续输出硬核干货。


一、并发编程基础

1. 线程和进程的区别?

线程(Thread)和进程(Process)的区别是操作系统和并发编程的基础概念,主要区别如下:

定义与本质

  • 进程 : 操作系统资源分配的基本单位 。一个进程是程序的一次执行过程,拥有独立的内存空间(堆、栈、数据段、代码段)和系统资源(如文件句柄、网络连接)。
  • 线程 : CPU调度的基本单位 ,是进程内的一个执行单元。同一进程的多个线程共享进程的资源 (内存、文件等),但拥有独立的栈和程序计数器。

资源开销

  • 进程 : 创建、切换、销毁开销大,因为需要分配独立的内存空间和资源。
  • 线程 : 开销小,因为共享进程资源,创建和切换只需保存少量寄存器状态。

内存与隔离性

  • 进程 : 内存相互隔离,一个进程崩溃不会直接影响 其他进程(稳定性高,但通信复杂)。
  • 线程 : 共享内存,一个线程崩溃可能导致整个进程崩溃 (稳定性低,但通信高效)。

通信方式

  • 进程 : 需要进程间通信(IPC)机制,如管道、消息队列、共享内存、Socket等。
  • 线程 : 可直接读写共享变量(需同步机制,如锁、volatile)。

并发性

  • 进程 : 宏观上并行(多进程同时运行),但上下文切换成本高。
  • 线程 : 轻量级并发,适合高并发任务(如Web服务器处理请求)。

类比理解

  • 进程 = 工厂 拥有独立的土地(内存)、原料(资源)、工人(线程)。
  • 线程 = 工厂中的工人 共享工厂资源,协作完成生产任务。

Java中的体现

  • 进程 :每个Java程序运行在一个JVM进程中。
  • 线程 :Java通过Thread类或Runnable接口创建线程,共享进程的堆内存。

面试回答示例

“进程是资源分配的最小单位,线程是CPU调度的最小单位。进程拥有独立的内存空间,切换开销大;线程共享进程资源,切换开销小,但需要处理同步问题。在Java中,多线程共享堆内存,适合高并发任务,而多进程更适合需要强隔离的场景。”

2. 并发和并行的区别?

这是一个非常核心的并发编程概念,并发 和并行 的区别可以用一个生动的比喻来概括:

核心区别:

  • 并发 :是处理多个任务的能力 (逻辑上同时发生)。它关乎结构 和问题分解 。
  • 并行 :是同时执行多个任务 (物理上同时发生)。它关乎执行 和资源利用 。

并发

  • 定义 :系统在一段时间内 交替处理多个任务,给人“同时”进行的错觉。在单核CPU时代,这就是实现多任务的唯一方式。
  • 关键 :任务间的快速切换 。系统通过时间片轮转,让每个任务都获得一点执行时间,从宏观上看,所有任务都在推进。
  • 目标 :提高系统的响应能力 和资源利用率 (例如,当任务A等待I/O时,切换到任务B执行,不让CPU空闲)。
  • 场景 :
  • 单核CPU上运行多个程序。
  • 一个Web服务器同时处理成千上万个用户请求(通过I/O多路复用或线程池快速切换)。
  • 你在电脑上一边听音乐,一边写文档,一边下载文件。

并行

  • 定义 :系统在同一个时刻 真正同时执行多个任务。
  • 前提 :必须有多核/多CPU或多台机器等多个计算资源 。
  • 关键 :任务被分解并分配到不同资源上同时执行 。
  • 目标 :缩短任务的整体完成时间 ,提高吞吐量。
  • 场景 :
  • 在多核CPU上,一个视频编码软件同时使用多个核心来渲染不同帧。
  • 使用分布式计算集群进行大数据分析(如Hadoop/Spark)。
  • 矩阵乘法中,不同部分的计算被分配到不同核心。

关系与联系

  1. 并行是并发的真子集 :并行一定是并发 (因为同时执行多个任务也意味着能处理多个任务),但并发不一定是并行 (单核上的并发就不是并行)。
  2. 并发是并行的基础 :一个设计良好的并发程序(任务可分解、无强依赖)更容易在并行硬件上获得性能提升 。如果程序本身不是并发设计的(一个巨大的串行任务),再多的CPU核心也无法并行执行它。
  3. 现代系统通常是两者的结合 :你的多核电脑上,既有多个进程在并行执行(每个进程可能跑在不同核心上),同时每个进程内部又可能通过多线程实现并发。

在编程中的体现

  • 实现并发 :多线程编程、异步I/O(async/await)、事件驱动模型。关键在于管理任务的状态和切换 ,处理竞态条件、死锁等问题。
  • 实现并行 :多进程编程(利用多核)、GPU计算、分布式计算。关键在于将问题分解为可独立执行的子任务 ,并处理任务分配、通信和结果合并 。

3. 创建线程有哪些方式?

在Java中,创建线程主要有以下几种方式:

1.继承 Thread 类

这是最基本的方式,通过继承 Thread 类并重写 ​​run()​​ 方法。

public class MyThread extends Thread { @Override public void run() { System.out.println("线程执行: " + Thread.currentThread().getName()); } } // 使用 public class Main { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); // 启动线程 } }

特点:

  • 简单直观
  • Java不支持多继承,所以这种方式限制了类的继承能力
  • 线程和任务耦合在一起

2.实现 Runnable 接口(推荐)

更常用的方式,实现 Runnable 接口,将任务逻辑与线程分离。

public class MyRunnable implements Runnable { @Override public void run() { System.out.println("线程执行: " + Thread.currentThread().getName()); } } // 使用 public class Main { public static void main(String[] args) { // 方式1:创建Thread对象时传入Runnable Thread thread = new Thread(new MyRunnable()); thread.start(); // 方式2:使用匿名内部类 Thread thread2 = new Thread(new Runnable() { @Override public void run() { System.out.println("匿名内部类方式"); } }); thread2.start(); // 方式3:使用Lambda表达式(Java 8+) Thread thread3 = new Thread(() -> { System.out.println("Lambda表达式方式"); }); thread3.start(); } }

优点:

  • 任务与线程解耦
  • 可以继承其他类
  • 更适合资源共享(多个线程可以共享同一个Runnable实例)

3.实现 Callable 接口

Callable 与 Runnable 类似,但有返回值,并且可以抛出异常。

import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class MyCallable implements Callable<String> { @Override public String call() throws Exception { Thread.sleep(1000); return "任务执行完成"; } } // 使用 public class Main { public static void main(String[] args) throws Exception { // 创建Callable实例 MyCallable callable = new MyCallable(); // 包装成FutureTask FutureTask<String> futureTask = new FutureTask<>(callable); // 创建线程并启动 Thread thread = new Thread(futureTask); thread.start(); // 获取结果(会阻塞直到任务完成) String result = futureTask.get(); System.out.println("结果: " + result); } }

特点:

  • 有返回值
  • 可以抛出异常
  • 通常与ExecutorService配合使用

4.使用线程池(实际开发中最常用)

通过 Executor 框架创建线程池,这是企业级应用中最推荐的方式。

import java.util.concurrent.*; public class Main { public static void main(String[] args) { // 1. 创建线程池 ExecutorService executor = Executors.newFixedThreadPool(5); // 2. 提交任务 // 使用Runnable executor.execute(() -> { System.out.println("Runnable任务执行"); }); // 使用Callable Future<String> future = executor.submit(() -> { Thread.sleep(1000); return "Callable任务结果"; }); // 3. 关闭线程池 executor.shutdown(); try { // 获取Callable的结果 String result = future.get(); System.out.println(result); } catch (Exception e) { e.printStackTrace(); } } }

5.使用 CompletableFuture(Java 8+)

Java 8 引入的异步编程方式,功能更强大。

import java.util.concurrent.CompletableFuture; public class Main { public static void main(String[] args) { // 异步执行任务 CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return "异步任务结果"; }); // 异步处理结果 future.thenAccept(result -> { System.out.println("接收到结果: " + result); }); // 等待任务完成 future.join(); } }

6.使用 Timer/TimerTask

用于定时任务,但功能有限,不推荐用于复杂场景。

import java.util.Timer; import java.util.TimerTask; public class Main { public static void main(String[] args) { Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { System.out.println("定时任务执行"); } }, 1000, 2000); // 延迟1秒,每2秒执行一次 } }

总结对比

方式

优点

缺点

适用场景

继承Thread

简单直观

不能继承其他类,任务与线程耦合

简单的测试或演示

实现Runnable

解耦任务与线程,可继承其他类

无返回值,不能抛出受检异常

大多数业务场景

实现Callable

有返回值,可抛出异常

使用稍复杂

需要获取执行结果的场景

线程池

资源复用,性能好,管理方便

配置相对复杂

生产环境,高并发场景

CompletableFuture

异步编程,链式调用,功能强大

学习成本较高

复杂的异步编程场景

Timer

简单定时

单线程,任务阻塞会影响其他任务

简单的定时任务

最佳实践建议

  1. 优先使用线程池 :避免频繁创建和销毁线程
  2. 推荐实现Runnable或Callable :而不是继承Thread
  3. Java 8+推荐使用CompletableFuture :进行异步编程
  4. 避免直接new Thread() :在生产环境中
// 生产环境推荐写法 ExecutorService executor = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors() * 2 ); // 提交任务 executor.submit(() -> { // 业务逻辑 }); // 或者使用CompletableFuture CompletableFuture.runAsync(() -> { // 业务逻辑 }, executor);

4. 并发编程的三大核心问题?

Java并发编程主要面临以下三大核心问题,它们是所有并发问题的基础:

1.可见性问题(Visibility Problem)

定义:

一个线程对共享变量的修改,另一个线程不能立即看到。

原因:

现代计算机为了提升性能,采用了多级缓存架构:

  • CPU有自己的缓存(L1、L2、L3)
  • 线程操作的是CPU缓存中的副本,而不是直接操作主内存
  • 缓存刷新到主内存的时间不确定

示例代码:

public class VisibilityProblem { private static boolean flag = true; public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { System.out.println("线程1开始执行"); while (flag) { // 空循环,等待flag变为false } System.out.println("线程1结束"); }); Thread t2 = new Thread(() -> { try { Thread.sleep(1000); // 等待1秒 } catch (InterruptedException e) { e.printStackTrace(); } flag = false; // 修改共享变量 System.out.println("线程2将flag改为false"); }); t1.start(); t2.start(); t1.join(); t2.join(); } }

可能的结果:

  • 线程2修改了flag为false
  • 但线程1可能永远看不到这个变化,导致无限循环
  • 这是因为线程1的CPU缓存中保存的是flag的旧值(true)

解决方案:

  1. 使用volatile关键字
private static volatile boolean flag = true;
  1. 使用synchronized同步块
private static boolean flag = true; private static final Object lock = new Object(); // 线程1 synchronized(lock) { while (flag) { ... } } // 线程2 synchronized(lock) { flag = false; }
  1. 使用Atomic类
private static AtomicBoolean flag = new AtomicBoolean(true);

2.原子性问题(Atomicity Problem)

定义:

一个或多个操作,要么全部执行成功,要么全部不执行,不会出现执行到一半的情况。

原因:

  • Java中的基本操作(如i++)在底层是多个步骤
  • 线程切换可能发生在这些步骤之间

示例代码:

public class AtomicityProblem { private static int count = 0; public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { for (int i = 0; i < 10000; i++) { count++; // 这不是原子操作! } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 10000; i++) { count++; // 这不是原子操作! } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("最终结果: " + count); // 可能小于20000 } }

count++ 的实际步骤:

  1. 读取count的值到寄存器
  2. 将寄存器中的值加1
  3. 将结果写回count

可能的问题:

线程1:读取count=0 线程2:读取count=0 线程1:计算0+1=1,写入count=1 线程2:计算0+1=1,写入count=1 结果:count=1(应该是2)

解决方案:

  1. 使用synchronized
private static int count = 0; private static final Object lock = new Object(); public static void increment() { synchronized(lock) { count++; } }
  1. 使用ReentrantLock
private static int count = 0; private static final ReentrantLock lock = new ReentrantLock(); public static void increment() { lock.lock(); try { count++; } finally { lock.unlock(); } }
  1. 使用Atomic类
private static AtomicInteger count = new AtomicInteger(0); public static void increment() { count.incrementAndGet(); }

3.有序性问题(Ordering Problem)

定义:

程序执行的顺序与代码编写的顺序不一致。

原因:

  • 编译器优化 :编译器可能重排指令
  • CPU指令重排 :CPU为了性能可能重排指令
  • 内存系统重排 :内存系统可能重排读写操作

示例代码(双重检查锁定的单例模式):

public class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { // 第一次检查 synchronized (Singleton.class) { if (instance == null) { // 第二次检查 instance = new Singleton(); // 问题在这里! } } } return instance; } }

问题分析:​​instance = new Singleton();​​ 实际包含三个步骤:

  1. 分配内存空间
  2. 初始化对象
  3. 将引用指向内存地址

由于指令重排,可能变成:

  1. 分配内存空间
  2. 将引用指向内存地址(此时instance不为null,但对象未初始化)
  3. 初始化对象

导致的问题:

线程A执行到步骤2时,线程B看到instance不为null,直接返回未完全初始化的对象。

解决方案:

  1. 使用volatile防止指令重排
private static volatile Singleton instance;
  1. 使用静态内部类
public class Singleton { private Singleton() {} private static class Holder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return Holder.INSTANCE; } }

综合示例:三大问题同时存在

public class ConcurrencyProblems { // 可见性问题:没有volatile private static boolean ready = false; // 原子性问题:没有同步 private static int number = 0; public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { while (!ready) { // 可能永远看不到ready的变化(可见性问题) } System.out.println("number = " + number); }); Thread t2 = new Thread(() -> { number = 42; // 可能被重排到ready=true之后(有序性问题) ready = true; // 可能被重排到number=42之前(有序性问题) }); t1.start(); t2.start(); Thread.sleep(1000); // 原子性问题演示 Counter counter = new Counter(); Thread t3 = new Thread(() -> { for (int i = 0; i < 10000; i++) { counter.increment(); // 非原子操作 } }); Thread t4 = new Thread(() -> { for (int i = 0; i < 10000; i++) { counter.increment(); // 非原子操作 } }); t3.start(); t4.start(); t3.join(); t4.join(); System.out.println("Counter value: " + counter.getValue()); // 可能小于20000 } static class Counter { private int value = 0; public void increment() { value++; // 非原子操作 } public int getValue() { return value; } } }

解决方案总结

问题类型

解决方案

示例

可见性

volatile、synchronized、Atomic类

volatile boolean flag

原子性

synchronized、Lock、Atomic类

AtomicInteger count

有序性

volatile

volatile Singleton instance

理解这三大问题是掌握Java并发编程的基础,只有理解了这些问题,才能正确选择和使用并发工具。

5. 线程的状态?

  • NEW:新建
  • RUNNABLE:可运行(就绪+运行)
  • BLOCKED:阻塞(等待监视器锁)
  • WAITING:等待(无时间限制)
  • TIMED_WAITING:超时等待
  • TERMINATED:终止

1.线程状态总览

/** * Java线程的6种状态(java.lang.Thread.State): * * 1. NEW(新建) - 线程被创建但未启动 * 2. RUNNABLE(可运行) - 线程正在JVM中执行 * 3. BLOCKED(阻塞) - 线程等待监视器锁 * 4. WAITING(等待) - 线程无限期等待其他线程通知 * 5. TIMED_WAITING(限时等待)- 线程在指定时间内等待 * 6. TERMINATED(终止) - 线程已执行完毕 * * 状态转换图: * * ┌──────┐ * │ NEW │ * └──┬───┘ * │ start() * ▼ * ┌─────────┐ * │RUNNABLE │◄──┐ * └──┬──────┘ │ * │ │ * 获得CPU│ │时间片用完/yield() * ▼ │ * ┌──────┐ │ * │运行中 │─────┘ * └──┬───┘ * │ * ▼ * ┌─────────────┐ * │TERMINATED │ * └─────────────┘ * * 阻塞/等待状态: * * RUNNABLE ──wait()/join()──► WAITING ──notify()/notifyAll()──► RUNNABLE * RUNNABLE ──sleep()/join(time)──► TIMED_WAITING ──超时/中断──► RUNNABLE * RUNNABLE ──竞争synchronized锁──► BLOCKED ──获得锁──► RUNNABLE */

图片来源《剑指Java》

2.详细状态解析与代码示例

2.1 NEW(新建状态)

public class NewStateExample { public static void main(String[] args) { // 创建线程但未调用start()方法 Thread thread = new Thread(() -> { System.out.println("线程执行中..."); }); System.out.println("线程状态: " + thread.getState()); // NEW System.out.println("是否存活: " + thread.isAlive()); // false // 此时线程对象已创建,但未分配系统资源 // 不能执行,只能启动或销毁 } }

2.2 RUNNABLE(可运行状态)

public class RunnableStateExample { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { // 线程执行逻辑 for (int i = 0; i < 5; i++) { System.out.println("执行次数: " + i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }); thread.start(); // 进入RUNNABLE状态 Thread.sleep(50); System.out.println("线程状态: " + thread.getState()); // RUNNABLE System.out.println("是否存活: " + thread.isAlive()); // true // RUNNABLE状态包含两个子状态: // 1. Ready(就绪):等待CPU时间片 // 2. Running(运行):正在执行 } }

2.3 BLOCKED(阻塞状态)

public class BlockedStateExample { private static final Object lock = new Object(); public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(() -> { synchronized (lock) { System.out.println("线程1获得锁,执行10秒"); try { Thread.sleep(10000); // 持有锁10秒 } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread thread2 = new Thread(() -> { System.out.println("线程2尝试获取锁..."); synchronized (lock) { System.out.println("线程2获得锁"); } }); thread1.start(); Thread.sleep(100); // 确保thread1先获得锁 thread2.start(); Thread.sleep(100); // 确保thread2开始执行 System.out.println("线程2状态: " + thread2.getState()); // BLOCKED // BLOCKED状态触发条件: // 1. 等待进入synchronized方法/代码块 // 2. 等待重新进入synchronized方法/代码块(不会发生) } }

2.4 WAITING(无限期等待状态)

public class WaitingStateExample { private static final Object lock = new Object(); public static void main(String[] args) throws InterruptedException { Thread waitingThread = new Thread(() -> { synchronized (lock) { try { System.out.println("线程进入WAITING状态"); lock.wait(); // 释放锁并无限期等待 System.out.println("线程被唤醒"); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread notifyingThread = new Thread(() -> { try { Thread.sleep(2000); // 等待2秒 synchronized (lock) { lock.notify(); // 唤醒等待的线程 System.out.println("发送通知"); } } catch (InterruptedException e) { e.printStackTrace(); } }); waitingThread.start(); Thread.sleep(100); // 确保waitingThread先执行 System.out.println("等待线程状态: " + waitingThread.getState()); // WAITING notifyingThread.start(); // 等待两个线程执行完毕 waitingThread.join(); notifyingThread.join(); // 进入WAITING状态的方法: // 1. Object.wait() - 需要先获得对象锁 // 2. Thread.join() - 等待其他线程终止 // 3. LockSupport.park() - 底层实现 } }

2.5 TIMED_WAITING(限时等待状态)

public class TimedWaitingStateExample { public static void main(String[] args) throws InterruptedException { // 示例1: Thread.sleep() Thread sleepThread = new Thread(() -> { try { System.out.println("线程开始sleep"); Thread.sleep(5000); // 睡眠5秒 System.out.println("线程sleep结束"); } catch (InterruptedException e) { e.printStackTrace(); } }); // 示例2: Object.wait(timeout) Object lock = new Object(); Thread waitThread = new Thread(() -> { synchronized (lock) { try { System.out.println("线程开始timed wait"); lock.wait(3000); // 等待3秒 System.out.println("线程wait结束"); } catch (InterruptedException e) { e.printStackTrace(); } } }); // 示例3: Thread.join(timeout) Thread workerThread = new Thread(() -> { try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } }); Thread joinThread = new Thread(() -> { try { System.out.println("开始join其他线程"); workerThread.join(2000); // 等待workerThread 2秒 System.out.println("join结束"); } catch (InterruptedException e) { e.printStackTrace(); } }); sleepThread.start(); Thread.sleep(100); System.out.println("sleepThread状态: " + sleepThread.getState()); // TIMED_WAITING waitThread.start(); Thread.sleep(100); System.out.println("waitThread状态: " + waitThread.getState()); // TIMED_WAITING workerThread.start(); joinThread.start(); Thread.sleep(100); System.out.println("joinThread状态: " + joinThread.getState()); // TIMED_WAITING // 进入TIMED_WAITING状态的方法: // 1. Thread.sleep(long millis) // 2. Object.wait(long timeout) // 3. Thread.join(long millis) // 4. LockSupport.parkNanos() // 5. LockSupport.parkUntil() } }

2.6 TERMINATED(终止状态)

public class TerminatedStateExample { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { System.out.println("线程开始执行"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程执行结束"); }); System.out.println("启动前状态: " + thread.getState()); // NEW thread.start(); Thread.sleep(500); System.out.println("执行中状态: " + thread.getState()); // TIMED_WAITING thread.join(); // 等待线程结束 System.out.println("结束后状态: " + thread.getState()); // TERMINATED System.out.println("是否存活: " + thread.isAlive()); // false // TERMINATED状态特点: // 1. 线程执行完run()方法 // 2. 线程抛出未捕获异常 // 3. 不能再次启动(调用start()会抛IllegalThreadStateException) // 尝试重新启动已终止的线程 try { thread.start(); // 抛出异常 } catch (IllegalThreadStateException e) { System.out.println("异常: " + e.getMessage()); } } }

3.线程状态转换实战

public class ThreadStateTransition { private static final Object lock = new Object(); public static void main(String[] args) throws InterruptedException { System.out.println("=== 线程状态转换演示 ==="); // 创建线程 Thread thread = new Thread(() -> { System.out.println("1. 线程启动,状态: " + Thread.currentThread().getState()); synchronized (lock) { System.out.println("2. 获得锁,执行任务"); try { Thread.sleep(1000); // TIMED_WAITING System.out.println("3. sleep结束"); lock.wait(2000); // TIMED_WAITING System.out.println("5. wait结束"); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("6. 线程即将结束"); }); System.out.println("A. 创建后状态: " + thread.getState()); // NEW thread.start(); Thread.sleep(50); System.out.println("B. 启动后状态: " + thread.getState()); // RUNNABLE或TIMED_WAITING Thread.sleep(100); System.out.println("C. sleep中状态: " + thread.getState()); // TIMED_WAITING // 创建另一个线程竞争锁 Thread blocker = new Thread(() -> { synchronized (lock) { System.out.println("4. 另一个线程获得锁"); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread.sleep(1200); // 等待第一个线程进入wait blocker.start(); Thread.sleep(100); System.out.println("D. wait中状态: " + thread.getState()); // TIMED_WAITING Thread.sleep(2200); // wait超时,但锁被占用 System.out.println("E. wait超时但锁被占: " + thread.getState()); // BLOCKED thread.join(); blocker.join(); System.out.println("F. 最终状态: " + thread.getState()); // TERMINATED } }

6. ThreadLocal原理与使用示例?

ThreadLocal 核心原理

图示

ThreadLocal 使用示例

示例1:基本使用

public class ThreadLocalBasicExample { private static final ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); public static void main(String[] args) { new Thread(() -> { threadLocal.set(1); System.out.println("thread-1: " + threadLocal.get()); }).start(); new Thread(() -> { threadLocal.set(2); System.out.println("thread-2: " + threadLocal.get()); }).start(); new Thread(() -> { System.out.println("thread-3: " + threadLocal.get()); }).start(); } }

示例2:Web应用中的用户上下文

public class UserContextHolder { // 存储用户信息 private static final ThreadLocal<User> currentUser = new ThreadLocal<>(); // 存储请求ID private static final ThreadLocal<String> requestId = new ThreadLocal<>(); // 存储语言环境 private static final ThreadLocal<Locale> locale = ThreadLocal.withInitial(() -> Locale.getDefault()); public static class User { private Long id; private String username; private String role; // getters/setters } // 设置当前用户 public static void setCurrentUser(User user) { currentUser.set(user); } // 获取当前用户 public static User getCurrentUser() { return currentUser.get(); } // 设置请求ID public static void setRequestId(String id) { requestId.set(id); } // 获取请求ID public static String getRequestId() { return requestId.get(); } // 清理所有 ThreadLocal 变量 public static void clear() { currentUser.remove(); requestId.remove(); locale.remove(); } } // 在过滤器中使用 public class UserContextFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { // 从请求中获取用户信息 User user = extractUserFromRequest(request); UserContextHolder.setCurrentUser(user); // 生成请求ID String requestId = UUID.randomUUID().toString(); UserContextHolder.setRequestId(requestId); // 继续处理请求 chain.doFilter(request, response); } finally { // 必须清理,避免内存泄漏 UserContextHolder.clear(); } } private User extractUserFromRequest(ServletRequest request) { // 实际应用中从 Session 或 Token 中获取 return new User(); } }

示例3:数据库连接管理

public class ConnectionManager { // 每个线程持有自己的数据库连接 private static final ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() { @Override protected Connection initialValue() { return createConnection(); } }; // 获取线程本地的连接 public static Connection getConnection() { return connectionHolder.get(); } // 开启事务 public static void beginTransaction() { try { Connection conn = getConnection(); conn.setAutoCommit(false); } catch (SQLException e) { throw new RuntimeException("开启事务失败", e); } } // 提交事务 public static void commit() { try { Connection conn = getConnection(); conn.commit(); conn.setAutoCommit(true); } catch (SQLException e) { throw new RuntimeException("提交事务失败", e); } } // 回滚事务 public static void rollback() { try { Connection conn = getConnection(); conn.rollback(); conn.setAutoCommit(true); } catch (SQLException e) { throw new RuntimeException("回滚事务失败", e); } } // 关闭连接(在线程结束时调用) public static void closeConnection() { Connection conn = connectionHolder.get(); if (conn != null) { try { conn.close(); } catch (SQLException e) { // 记录日志 } finally { connectionHolder.remove(); } } } private static Connection createConnection() { // 创建数据库连接 return null; // 实际返回 Connection 对象 } }

示例4:性能监控

public class PerformanceMonitor { // 记录方法调用耗时 private static final ThreadLocal<Long> startTime = new ThreadLocal<>(); // 记录调用栈深度 private static final ThreadLocal<Integer> callDepth = ThreadLocal.withInitial(() -> 0); // 记录 SQL 执行次数 private static final ThreadLocal<Integer> sqlCount = ThreadLocal.withInitial(() -> 0); public static void start(String methodName) { startTime.set(System.currentTimeMillis()); callDepth.set(callDepth.get() + 1); StringBuilder indent = new StringBuilder(); for (int i = 0; i < callDepth.get(); i++) { indent.append(" "); } System.out.println(indent + "▶ " + methodName); } public static void end(String methodName) { long duration = System.currentTimeMillis() - startTime.get(); StringBuilder in
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/9 15:17:55

NHSE:三步解锁《动物森友会》无限创造力的存档编辑器

NHSE&#xff1a;三步解锁《动物森友会》无限创造力的存档编辑器 【免费下载链接】NHSE Animal Crossing: New Horizons save editor 项目地址: https://gitcode.com/gh_mirrors/nh/NHSE 动物森友会存档编辑器NHSE是一款专为《集合啦&#xff01;动物森友会》玩家打造的…

作者头像 李华
网站建设 2026/5/9 15:17:52

CANN HIXL示例指南

简介 【免费下载链接】hixl HIXL&#xff08;Huawei Xfer Library&#xff09;是一个灵活、高效的昇腾单边通信库&#xff0c;面向集群场景提供简单、可靠、高效的点对点数据传输能力。 项目地址: https://gitcode.com/cann/hixl 本项目提供了C和Python的调用样例&#…

作者头像 李华
网站建设 2026/5/9 15:17:23

Kubernetes Job与CronJob深度解析与实践

Kubernetes Job与CronJob深度解析与实践 Job与CronJob概述 在Kubernetes中&#xff0c;Job用于运行一次性任务&#xff0c;而CronJob则用于运行定时任务。本文将深入探讨Job和CronJob的核心概念、配置方法和最佳实践。 Job核心概念 1. 基本Job配置 apiVersion: batch/v1 kind: …

作者头像 李华
网站建设 2026/5/9 15:14:31

CANN/ops-nn神经网络算子库

ops-nn 【免费下载链接】ops-nn 本项目是CANN提供的神经网络类计算算子库&#xff0c;实现网络在NPU上加速计算。 项目地址: https://gitcode.com/cann/ops-nn &#x1f525;Latest News [2026/01] 新增QuickStart&#xff0c;指导新手零基础入门算子项目部署&#xff…

作者头像 李华
网站建设 2026/5/9 15:12:45

基于Astro+Starlight构建开源项目中文文档站:架构、本地化与自动化实践

1. 项目概述&#xff1a;OpenClaw 中文网的建设初衷与价值最近在折腾一个挺有意思的开源项目——OpenClaw&#xff0c;一个能帮你处理日常杂事的个人AI智能体。它最吸引我的地方是&#xff0c;你可以把它部署在自己的电脑或者服务器上&#xff0c;通过微信、钉钉这些你天天在用…

作者头像 李华
网站建设 2026/5/9 15:11:45

CANN/ops-nn硬收缩激活函数算子

aclnnHardshrink 【免费下载链接】ops-nn 本项目是CANN提供的神经网络类计算算子库&#xff0c;实现网络在NPU上加速计算。 项目地址: https://gitcode.com/cann/ops-nn &#x1f4c4; 查看源码 产品支持情况 产品是否支持Ascend 950PR/Ascend 950DTAtlas A3 训练系列…

作者头像 李华