Java 并发编程核心知识点

一、并发编程基础概念

1. 进程与线程

核心定义
  • 进程:程序运行的实例,是操作系统资源分配(以内存为主)的最小单位。进程拥有独立的内存空间、IO 资源等,进程间相互独立,通信复杂(IPC 机制或网络协议)。
  • 线程:CPU 调度的最小单位,依赖进程存在,是进程内的执行实体。线程仅拥有程序计数器、寄存器、栈等必要资源,共享所属进程的全部资源,线程间通信简单(共享内存)。
核心区别
对比维度 进程 线程
资源分配 最小单位 无独立资源(共享进程资源)
调度单位 操作系统调度进程 CPU 调度线程
通信复杂度 复杂(IPC / 网络协议) 简单(共享内存)
上下文切换成本

2. 并发与并行

  • 并发(Concurrent):同一时间单位内交替执行多个任务(微观串行,宏观并行)。例如单 CPU 核心下多线程切换执行。
  • 并行(Parallel):同一时刻同时执行多个任务。例如多核 CPU 下,每个核心独立调度线程执行。

3. 上下文切换

  • 定义:CPU 从一个线程 / 进程切换到另一个线程 / 进程时,保存当前线程 / 进程的 CPU 状态(寄存器、程序计数器等),加载目标线程 / 进程状态的过程。
  • 成本:一次上下文切换需 5000~20000 个时钟周期,远高于普通指令执行成本,是并发编程性能损耗的核心原因之一。
  • 触发场景:线程 / 进程切换、系统调用、IO 阻塞等。

二、Java 线程核心知识点

1. Java 线程的创建方式

  • 方式 1:继承 Thread 类,重写 run () 方法(线程与任务耦合)。

    Thread t1 = new Thread("t1") {
    @Override
    public void run() {
    System.out.println("Thread run");
    }
    };
    t1.start();

  • 方式 2:实现 Runnable 接口,配合 Thread 类(线程与任务解耦,推荐)。

    Runnable task = () -> System.out.println("Runnable run");
    Thread t2 = new Thread(task, "t2");
    t2.start();

扩展方式(本质基于 Runnable)
  • 方式 3:实现 Callable 接口,配合 FutureTask(支持任务返回值)。

    FutureTask<Integer> task = new FutureTask<>(() -> {
    System.out.println("Callable run");
    return 100;
    });
    new Thread(task, "t3").start();
    Integer result = task.get(); // 阻塞获取结果

面试易错点
  • 误区:认为 Callable 是独立的创建方式。本质:Callable 需通过 FutureTask 包装为 Runnable,再交给 Thread 执行,属于 Runnable 方式的扩展。

2. Java 线程生命周期(6 种状态)

状态定义(Thread.State 枚举)
  1. NEW:线程对象创建但未调用 start () 方法。
  2. RUNNABLE:包含就绪(ready)和运行中(running)状态,线程等待 CPU 调度或正在执行。
  3. BLOCKED:线程阻塞于锁(synchronized 竞争失败)。
  4. WAITING:线程等待其他线程通知(Object.wait ()、Thread.join () 等),无超时时间。
  5. TIMED_WAITING:带超时时间的等待(Thread.sleep (long)、Object.wait (long) 等)。
  6. TERMINATED:线程执行完毕或异常终止。

3. 线程核心方法

关键方法对比
方法名 静态属性 功能说明 注意事项
start() 启动线程,进入就绪状态,触发 run () 执行 不可重复调用,否则抛 IllegalThreadStateException
run() 线程执行的任务逻辑 直接调用仅为普通方法,不启动新线程
join() 等待线程执行完毕 用于线程同步
sleep(long n) 当前线程休眠 n 毫秒,不释放锁 可被 interrupt () 中断,抛出 InterruptedException
yield() 让出 CPU 使用权,进入就绪状态 仅为提示,调度器可忽略
interrupt() 中断线程 阻塞线程会抛异常并清空中断标记,运行中线程仅设标记
isInterrupted() 判断线程是否被中断 不清除中断标记
interrupted() 判断当前线程是否被中断 清除中断标记
常用方法区别
  • sleep vs yield
    • sleep:主动休眠,释放 CPU,不释放锁,休眠时间可指定。
    • yield:主动让出 CPU,不释放锁,无休眠时间,仅让优先级相同或更高的线程有机会执行。
  • wait vs notify/notifyAll
    • 必须在 synchronized 代码块中调用,调用时释放对象锁。
    • wait:线程进入等待队列,需被 notify/notifyAll 唤醒。
    • notify:唤醒一个等待线程,notifyAll:唤醒所有等待线程。

4. 守护线程

  • 定义:服务于非守护线程的线程,当所有非守护线程结束时,守护线程无论是否执行完毕都会被强制终止。
  • 应用场景:垃圾回收器、心跳检测、事件监听等后台任务。
  • 创建方式thread.setDaemon(true)(需在 start () 前调用)。

三、并发编程核心机制

1. 线程间通信方式

核心方式
通信方式 适用场景 特点
共享内存(volatile / 共享变量) 简单数据传递 需保证可见性、原子性
管道流(PipedInputStream/PipedOutputStream) 线程间字节 / 字符传输 基于内存,无需磁盘 IO
等待 / 通知机制(wait/notify、LockSupport) 线程协作(生产者 - 消费者) 高效,减少轮询开销
Thread.join() 线程顺序执行 同步等待线程完成
等待 / 通知机制规范
  • 等待方:获取锁 → 条件不满足则 wait () → 被唤醒后再次检查条件 → 执行逻辑。

    synchronized (lock) {
    while (!condition) {
    lock.wait(); // 避免虚假唤醒,必须用while循环
    }
    // 业务逻辑
    }

  • 通知方:获取锁 → 修改条件 → 通知所有等待线程。

    synchronized (lock) {
    condition = true;
    lock.notifyAll(); // 优先用notifyAll,避免唤醒错误线程
    }

LockSupport(park/unpark)
  • 无需锁支持,可在任意位置阻塞 / 唤醒线程。

  • 支持先 unpark 后 park(不会阻塞),适合复杂线程协作场景。

    Thread t = new Thread(() -> {
    LockSupport.park(); // 阻塞
    System.out.println("被唤醒");
    });
    t.start();
    LockSupport.unpark(t); // 唤醒

2. 线程同步机制

核心同步方式
  • volatile
    • 保证可见性(一个线程修改后,其他线程立即可见)和有序性(禁止指令重排序)。
    • 不保证原子性,适用于 "单写多读" 场景。
  • synchronized
    • 保证原子性、可见性、有序性,是重量级锁(JDK1.6 后优化为偏向锁、轻量级锁、重量级锁)。
    • 可修饰方法、代码块,锁对象为类对象或实例对象。
  • Lock 接口(ReentrantLock 等)
    • 可重入、可中断、可超时、支持公平锁 / 非公平锁,灵活性高于 synchronized。
    • 需手动释放锁(try-finally 中调用 unlock ())。
原子操作
  • CAS(Compare and Swap):无锁同步机制,通过硬件指令保证原子性。
  • Atomic 类:基于 CAS 实现,如 AtomicInteger、AtomicReference 等,支持原子性的增删改操作。

四、并发设计模式

1. 优雅终止线程:两阶段终止模式

核心思想
  • 第一阶段:发送终止请求(通过 interrupt () 唤醒阻塞线程)。
  • 第二阶段:线程检查终止标志位,执行清理工作后退出。
实现代码
复制代码
public class MonitorThread extends Thread {
    private volatile boolean terminated = false; // 终止标志位

    @Override
    public void run() {
        while (!terminated && !Thread.interrupted()) {
            try {
                System.out.println("监控中...");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // 重置中断标志
                break;
            }
        }
        System.out.println("执行清理工作...");
    }

    public void terminate() {
        terminated = true;
        this.interrupt(); // 唤醒阻塞线程
    }
}
线程池终止
  • shutdown():拒绝新任务,等待队列中任务执行完毕后关闭。
  • shutdownNow():拒绝新任务,中断正在执行的任务,返回未执行的任务列表。

2. 避免共享:三大设计模式

模式 核心思想 应用场景 注意事项
不变性模式 对象创建后状态不可修改(final 修饰属性 + 只读方法) 缓存、配置信息、值对象 需保证属性对象也不可变
写时复制模式(Copy-on-Write) 写操作时复制副本,修改后替换原数据 读多写少场景(如路由表) 写操作消耗内存,适用于写少场景
线程本地存储模式(ThreadLocal) 为每个线程分配独立存储,数据隔离 保存线程上下文(如用户会话) 线程池环境需手动 remove (),避免内存泄漏
ThreadLocal 内存泄漏问题
  • 原因:ThreadLocalMap 中 Entry 的 key 为弱引用,线程结束后若未手动清理,value 可能被长期引用。
  • 解决方案:使用后调用threadLocal.remove()清理。

3. 多线程分工模式

模式 核心思想 应用场景 实现方式
Thread-Per-Message 为每个任务创建独立线程 低并发异步场景(如定时任务) new Thread ()(高并发需线程池)
Worker Thread 线程池复用线程,避免频繁创建销毁 高并发场景(如服务端处理请求) Executors.newFixedThreadPool () 等
生产者 - 消费者模式 生产者生产任务入队,消费者从队列取任务执行 任务解耦、削峰填谷 BlockingQueue + 线程池
生产者 - 消费者模式实现
复制代码
public class ProducerConsumer {
    private static final BlockingQueue<String> queue = new ArrayBlockingQueue<>(5);

    public static void main(String[] args) {
        // 生产者
        new Thread(() -> {
            try {
                while (true) {
                    queue.put("任务");
                    System.out.println("生产任务,队列大小:" + queue.size());
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        // 消费者
        new Thread(() -> {
            try {
                while (true) {
                    String task = queue.take();
                    System.out.println("消费任务,队列大小:" + queue.size());
                    Thread.sleep(2000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

4. 多线程版本 if 模式

模式 核心思想 区别 应用场景
守护挂起模式(Guarded Suspension) 条件不满足时线程等待,直到条件满足 等待条件满足后执行 join ()、Future 实现
避免执行模式(Balking) 条件不满足时直接返回,不等待 无需等待,直接放弃 自动保存、单次初始化
相关推荐
雾削木5 小时前
使用 ESPHome 的核心指令
java·前端·javascript·单片机·嵌入式硬件
xyq20245 小时前
抽象工厂模式
开发语言
冰暮流星5 小时前
javascript如何实现将一个整数倒过来输出
开发语言·前端·javascript
Dylan的码园5 小时前
深入浅出Java排序:从基础算法到实战优化(下)
java·算法·排序算法
凤年徐5 小时前
C++ STL list 容器详解:使用与模拟实现
开发语言·c++·后端·list
中二病码农不会遇见C++学姐5 小时前
文明6 Mod制作核心组件关系解密:从XML到游戏的奇幻漂流
java·运维·服务器·游戏
我爱娃哈哈5 小时前
SpringBoot + ResponseBodyEmitter 实时异步流式推送:告别轮询,让数据推送更高效
java·spring boot·后端
白宇横流学长5 小时前
基于 SpringBoot 的足球俱乐部管理系统设计与实现【源码+文档】
java·spring boot·后端
lead520lyq5 小时前
Ethers.js发布合约及查询合约
开发语言·后端·区块链