一篇讲清楚 Java 并发编程

前言

并发编程是 Java 开发中不可或缺的技能,也是面试中的高频考点。本文将系统性地讲解 Java 并发编程的核心知识体系,帮助你建立完整的并发编程知识框架。


一、并发基础概念

1.1 进程 vs 线程

特性进程线程
定义操作系统资源分配的基本单位CPU 调度和执行的基本单位
资源占用独立内存空间共享进程资源
通信IPC(管道、消息队列等)共享内存、通信简单
切换开销大小

1.2 并发 vs 并行

  • 并发:同一时间段内,多个任务都在进行(时间片轮转)
  • 并行:同一时刻,多个任务同时进行(多核 CPU)

1.3 线程状态

public enum State {
    NEW,           // 新建状态
    RUNNABLE,      // 可运行状态
    BLOCKED,       // 阻塞状态
    WAITING,       // 等待状态
    TIMED_WAITING, // 超时等待状态
    TERMINATED     // 终止状态
}

二、线程的创建与使用

2.1 创建线程的四种方式

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

// 使用
MyThread thread = new MyThread();
thread.start();
方式二:实现 Runnable 接口
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("线程运行中");
    }
}

// 使用
Thread thread = new Thread(new MyRunnable());
thread.start();
方式三:实现 Callable 接口(有返回值)
public class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "线程执行结果";
    }
}

// 使用
FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
Thread thread = new Thread(futureTask);
thread.start();
String result = futureTask.get(); // 阻塞获取结果
方式四:使用线程池(推荐)
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.submit(() -> {
    System.out.println("线程池执行任务");
});
executor.shutdown();

2.2 线程的优先级

thread.setPriority(Thread.MAX_PRIORITY); // 10
thread.setPriority(Thread.MIN_PRIORITY); // 1
thread.setPriority(Thread.NORM_PRIORITY); // 5(默认)

三、Synchronized 关键字

3.1 三种使用方式

实例方法锁
public class Counter {
    private int count = 0;

    // 锁住的是 this 对象
    public synchronized void increment() {
        count++;
    }
}
静态方法锁
public class Counter {
    private static int count = 0;

    // 锁住的是 Class 对象
    public static synchronized void increment() {
        count++;
    }
}
代码块锁
public class Counter {
    private int count = 0;
    private final Object lock = new Object();

    public void increment() {
        synchronized (lock) { // 锁住指定的对象
            count++;
        }
    }
}

3.2 Synchronized 原理

  • Monitor 监视器:每个对象都有一个 Monitor
  • 字节码层面:monitorenter 和 monitorexit
  • 锁升级过程:偏向锁 → 轻量级锁 → 重量级锁

四、Volatile 关键字

4.1 作用

public class VolatileExample {
    // 保证可见性和有序性,但不保证原子性
    private volatile boolean flag = false;

    public void setFlag() {
        flag = true; // 立即对其他线程可见
    }

    public void checkFlag() {
        while (!flag) {
            // 等待 flag 变为 true
        }
    }
}

4.2 Volatile vs Synchronized

特性VolatileSynchronized
可见性✓✓
有序性✓✓
原子性✗✓
阻塞✗✓

五、CAS 与 AQS

5.1 CAS(Compare And Swap)

// CAS 原理示例(伪代码)
public boolean compareAndSet(int expect, int update) {
    if (value == expect) {
        value = update;
        return true;
    }
    return false;
}

ABA 问题:解决方式是使用版本号 AtomicStampedReference

5.2 AQS(AbstractQueuedSynchronizer)

AQS 是 Java 并发包的基石,核心思想:

  • 状态变量:volatile int state
  • CLH 队列:等待线程的队列
  • 独占/共享:两种模式
// ReentrantLock 基于 AQS 实现
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
    // 临界区代码
} finally {
    lock.unlock();
}

六、并发工具类

6.1 CountDownLatch(倒计时门闩)

CountDownLatch latch = new CountDownLatch(3);

// 启动 3 个线程
for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        try {
            // 执行任务
        } finally {
            latch.countDown(); // 计数减 1
        }
    }).start();
}

latch.await(); // 等待计数器归零
System.out.println("所有任务完成");

6.2 CyclicBarrier(循环栅栏)

CyclicBarrier barrier = new CyclicBarrier(3, () -> {
    System.out.println("所有线程到达屏障点");
});

for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        try {
            // 执行任务
            barrier.await(); // 等待其他线程
        } catch (Exception e) {
            e.printStackTrace();
        }
    }).start();
}

6.3 Semaphore(信号量)

Semaphore semaphore = new Semaphore(5); // 允许 5 个线程同时访问

for (int i = 0; i < 10; i++) {
    new Thread(() -> {
        try {
            semaphore.acquire(); // 获取许可
            // 访问共享资源
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semaphore.release(); // 释放许可
        }
    }).start();
}

6.4 Exchanger(交换器)

Exchanger<String> exchanger = new Exchanger<>();

new Thread(() -> {
    try {
        String data = "数据A";
        String received = exchanger.exchange(data);
        System.out.println("线程A收到: " + received);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}).start();

new Thread(() -> {
    try {
        String data = "数据B";
        String received = exchanger.exchange(data);
        System.out.println("线程B收到: " + received);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}).start();

七、线程池

7.1 为什么使用线程池

  • 降低资源消耗:重复利用已创建的线程
  • 提高响应速度:任务到达时直接执行
  • 便于管理:统一分配、调优和监控

7.2 线程池核心参数

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    5,                          // corePoolSize: 核心线程数
    10,                         // maximumPoolSize: 最大线程数
    60L,                        // keepAliveTime: 非核心线程存活时间
    TimeUnit.SECONDS,           // 时间单位
    new LinkedBlockingQueue<>(100), // 工作队列
    Executors.defaultThreadFactory(),  // 线程工厂
    new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);

7.3 四种拒绝策略

策略说明
AbortPolicy(默认)抛出异常
CallerRunsPolicy调用者所在线程执行
DiscardPolicy直接丢弃
DiscardOldestPolicy丢弃队列最前面的任务

7.4 合理配置线程池

// CPU 密集型
int n = Runtime.getRuntime().availableProcessors();
poolSize = n + 1

// IO 密集型
poolSize = n * 2

7.5 禁止使用 Executors 创建

// 不推荐(OOM 风险)
ExecutorService executor1 = Executors.newFixedThreadPool(10);
ExecutorService executor2 = Executors.newCachedThreadPool();

// 推荐:明确参数
ThreadPoolExecutor executor = new ThreadPoolExecutor(...);

八、锁框架

8.1 ReentrantLock vs Synchronized

特性ReentrantLockSynchronized
可中断✓✗
公平锁✓✗(非公平)
超时获取✓✗
条件变量支持多个单一
自动释放手动自动

8.2 ReadWriteLock(读写锁)

ReadWriteLock rwLock = new ReentrantReadWriteLock();

// 读锁(共享锁)
rwLock.readLock().lock();
try {
    // 读操作
} finally {
    rwLock.readLock().unlock();
}

// 写锁(排他锁)
rwLock.writeLock().lock();
try {
    // 写操作
} finally {
    rwLock.writeLock().unlock();
}

8.3 StampedLock(印章锁)

StampedLock lock = new StampedLock();

// 乐观读
long stamp = lock.tryOptimisticRead();
// 读取数据
if (!lock.validate(stamp)) {
    // 验证失败,升级为悲观读
    stamp = lock.readLock();
    try {
        // 重新读取
    } finally {
        lock.unlockRead(stamp);
    }
}

九、并发容器

9.1 ConcurrentHashMap

ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
map.put("key", "value");

// Java 8+ 原子操作
map.computeIfAbsent("key", k -> "default");
map.merge("key", "value", (oldVal, newVal) -> oldVal + newVal);

9.2 CopyOnWriteArrayList

CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("item"); // 写时复制

// 适合读多写少的场景
for (String item : list) {
    // 读取不需要加锁
}

9.3 BlockingQueue

// ArrayBlockingQueue: 有界队列
BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);

// LinkedBlockingQueue: 可选有界
BlockingQueue<String> queue2 = new LinkedBlockingQueue<>(100);

// PriorityBlockingQueue: 优先级队列
BlockingQueue<Integer> queue3 = new PriorityBlockingQueue<>();

// SynchronousQueue: 同步队列(不存储元素)
BlockingQueue<String> queue4 = new SynchronousQueue<>();

十、原子类

10.1 基本类型原子类

AtomicInteger atomicInt = new AtomicInteger(0);
atomicInt.incrementAndGet();    // ++i
atomicInt.getAndIncrement();    // i++
atomicInt.addAndGet(5);         // i += 5
atomicInt.compareAndSet(5, 10); // CAS

10.2 引用类型原子类

AtomicReference<User> userRef = new AtomicReference<>();
User user = new User("Alice");
userRef.set(user);
userRef.compareAndSet(user, newUser);

10.3 数组类型原子类

AtomicIntegerArray array = new AtomicIntegerArray(10);
array.getAndSet(0, 100);
array.incrementAndGet(1);

10.4 对象属性修改原子类

AtomicIntegerFieldUpdater<User> updater =
    AtomicIntegerFieldUpdater.newUpdater(User.class, "age");

User user = new User();
updater.getAndIncrement(user);

10.5 原子累加器(Java 8+)

LongAdder adder = new LongAdder();
adder.increment(); // 性能优于 AtomicLong

LongAccumulator accumulator = new LongAccumulator((x, y) -> x + y, 0);
accumulator.accumulate(10);

十一、常见问题与解决方案

11.1 死锁

产生原因:多个线程互相持有对方需要的资源

避免方法:

  1. 固定加锁顺序
  2. 使用 tryLock() 设置超时
  3. 使用死锁检测机制
// 超时获取锁
if (lock1.tryLock(1, TimeUnit.SECONDS)) {
    try {
        if (lock2.tryLock(1, TimeUnit.SECONDS)) {
            try {
                // 执行业务
            } finally {
                lock2.unlock();
            }
        }
    } finally {
        lock1.unlock();
    }
}

11.2 活锁

现象:线程不断改变状态,但无法继续执行

解决:引入随机性或重试机制

11.3 线程安全问题排查

  1. 查看线程堆栈:jstack <pid>
  2. 查看内存状态:jmap -histo <pid>
  3. 使用工具:VisualVM、Arthas

十二、最佳实践

12.1 编码建议

  1. 同步块范围最小化:减少锁持有时间
  2. 使用线程局部变量:ThreadLocal
  3. 避免使用 stop/suspend:使用 interrupt
  4. 及时释放锁:使用 try-finally

12.2 性能优化

  1. 减小锁粒度:分段锁(ConcurrentHashMap 思想)
  2. 读写分离:ReadWriteLock
  3. 无锁编程:CAS、原子类
  4. 协程:Java 21+ 虚拟线程

12.3 Java 21+ 虚拟线程

// Java 21+
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    executor.submit(() -> {
        // 虚拟线程执行任务
    });
}

总结

Java 并发编程是一个庞大的知识体系,本文涵盖了从基础概念到高级特性的核心内容。学习并发编程的关键是:

  1. 理解基础:掌握线程、锁、内存模型等核心概念
  2. 多实践:通过代码加深理解
  3. 看源码:学习 JDK 并发包的实现原理
  4. 关注更新:Java 虚拟线程等新特性

希望这篇文章能帮助你系统地掌握 Java 并发编程!


参考资料

  • 《Java 并发编程的艺术》
  • 《深入理解 Java 虚拟机》
  • Java 并发包源码(JDK)
Logo

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

更多推荐