前置知识: Java

并发编程基础

00:00
3 min Intermediate 2026/6/14

线程、锁与并发工具

概述

Java 并发编程是多线程环境下编写正确程序的基础。本文从线程创建、同步机制到线程间通信,系统介绍 Java 并发编程的核心概念。理解线程安全、锁机制和内存模型是编写高质量并发代码的前提。

基础概念

线程状态

状态说明
NEW线程已创建但未启动
RUNNABLE可运行状态(包括运行中和就绪)
BLOCKED阻塞于锁
WAITING无限期等待(如 Object.wait)
TIMED_WAITING有限期等待(如 Thread.sleep)
TERMINATED线程执行完毕

线程安全等级

等级说明示例
不可变对象创建后状态可变String, Integer
安全任何环境下都线程安全AtomicInteger
相对安全操作安全,复合操作需额外同步Vector, HashTable
线程安全多线程下需外部同步ArrayList, HashMap

Java 内存模型(JMM)

  • 可见性:一个线程修改共享变量后,其他线程能立即看到修改
  • 操作不可被中断,要么全部执行要么不执行
  • 有序性程序顺序符合预期,编译器处理器不会随意重排

快速上手

线程创建

// 方式一:继承 Thread 类
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程运行: " + getName());
    }
}
new MyThread().start();

// 方式二:实现 Runnable 接口(推荐)
Thread t = new Thread(() -> {
    System.out.println("线程运行: " + Thread.currentThread().getName());
}, "my-thread");
t.start();

// 方式三:Callable + FutureTask(有返回值)
FutureTask<String> task = new FutureTask<>(() -> {
    Thread.sleep(1000);
    return "计算结果";
});
new Thread(task).start();
String result = task.get(); // 阻塞等待结果

// 方式四:虚拟线程(Java 21+)
Thread vt = Thread.startVirtualThread(() -> {
    System.out.println("虚拟线程运行");
});

synchronized 同步

// 同步代码块:指定锁对象
synchronized (lock) {
    // 临界区:同一时刻只有一个线程能进入
    balance += amount;
}

// 同步实例方法:锁为 this
public synchronized void withdraw(int amount) {
    if (balance >= amount) {
        balance -= amount;
    }
}

// 同步静态方法:锁为 Class 对象
public synchronized static int getInstanceCount() {
    return count;
}

volatile 关键字

// volatile 保证可见性和有序性,但不保证原子性
public class StatusFlag {
    private volatile boolean running = true;

    public void stop() {
        running = false; // 其他线程立即可见
    }

    public void work() {
        while (running) { // 每次读取最新值
            doWork();
        }
    }
}

// 注意:volatile 不能保证复合操作的原子性
private volatile int counter = 0;
counter++; // 不安全!包含读取、加1、写入三个操作

详细用法

ReentrantLock

// ReentrantLock 比 synchronized 更灵活
private final ReentrantLock lock = new ReentrantLock();

public void transfer(Account from, Account to, int amount) {
    lock.lock();
    try {
        from.debit(amount);
        to.credit(amount);
    } finally {
        lock.unlock(); // 必须在 finally 中释放
    }
}

// 可中断锁:响应中断
lock.lockInterruptibly();

// 定时锁:避免死锁
if (lock.tryLock(5, TimeUnit.SECONDS)) {
    try { doWork(); }
    finally { lock.unlock(); }
} else {
    // 获取锁超时,执行备选逻辑
}

// 公平锁:按等待顺序获取
ReentrantLock fairLock = new ReentrantLock(true);

线程间通信

// wait/notify 机制
class BoundedBuffer {
    private final Queue<String> queue = new LinkedList<>();
    private final int capacity = 10;

    // 生产者
    public synchronized void put(String item) throws InterruptedException {
        while (queue.size() == capacity) {
            wait(); // 队列满时等待
        }
        queue.add(item);
        notifyAll(); // 通知消费者
    }

    // 消费者
    public synchronized String take() throws InterruptedException {
        while (queue.isEmpty()) {
            wait(); // 队列空时等待
        }
        String item = queue.poll();
        notifyAll(); // 通知生产者
        return item;
    }
}

线程池基础

// 使用 ExecutorService 管理线程
ExecutorService executor = Executors.newFixedThreadPool(4);

// 提交任务
Future<String> future = executor.submit(() -> {
    return fetchData();
});

// 获取结果
try {
    String result = future.get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
    future.cancel(true); // 超时取消
}

// 关闭线程池
executor.shutdown();
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
    executor.shutdownNow(); // 强制关闭
}

常见场景

单例模式(线程安全)

// 双重检查锁定(DCL)
public class Singleton {
    private static volatile Singleton instance; // volatile 防止指令重排

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {              // 第一次检查(无锁)
            synchronized (Singleton.class) {
                if (instance == null) {      // 第二次检查(有锁)
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

// 更简洁的方式:使用枚举
public enum SingletonEnum {
    INSTANCE;
    public void doSomething() { }
}

生产者-消费者模式

// 使用 BlockingQueue 实现生产者-消费者
BlockingQueue<String> queue = new ArrayBlockingQueue<>(100);

// 生产者线程
Thread producer = new Thread(() -> {
    try {
        while (true) {
            String data = fetchData();
            queue.put(data); // 队列满时自动阻塞
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});

// 消费者线程
Thread consumer = new Thread(() -> {
    try {
        while (true) {
            String data = queue.take(); // 队列空时自动阻塞
            processData(data);
        }
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});

producer.start();
consumer.start();

注意事项

  • wait() 必须在 synchronized 调用,否则抛出 IllegalMonitorStateException
  • wait/notify 应该使用 while 循环检查条件,不要使用 if(防止虚假唤醒)
  • synchronized 和 ReentrantLock 不要混用选择一种即可
  • 线程池关闭时应先 shutdown 再 awaitTermination,最后 shutdownNow
  • 避免在持有锁时调用外部方法,可能导致死锁
  • 线程中断应通过 interrupt 机制协作,不要使用已废弃的 stop/suspend

进阶用法

读写锁

// 读多写少场景使用 ReadWriteLock
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Map<String, User> cache = new HashMap<>();

// 读操作:多个线程可同时读
public User get(String key) {
    rwLock.readLock().lock();
    try {
        return cache.get(key);
    } finally {
        rwLock.readLock().unlock();
    }
}

// 写操作:独占锁
public void put(String key, User user) {
    rwLock.writeLock().lock();
    try {
        cache.put(key, user);
    } finally {
        rwLock.writeLock().unlock();
    }
}

ThreadLocal 线程本地变量

// ThreadLocal 为每个线程维护独立的变量副本
private static final ThreadLocal<SimpleDateFormat> dateFormat =
    ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));

// 使用
String formatted = dateFormat.get().format(new Date());

// 线程池中使用 ThreadLocal 必须清理
executor.submit(() -> {
    try {
        dateFormat.get().format(date);
    } finally {
        dateFormat.remove(); // 防止内存泄漏
    }
});

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式