一、并发编程基础概念

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)条件不满足时直接返回,不等待无需等待,直接放弃自动保存、单次初始化
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐