Skip to content

并发编程

程序员的进阶门槛:高并发时代,你准备好了吗? 🚀

🎯 场景:为什么需要并发?

┌─────────────────────────────────────────────────────────────────┐
│                      串行 vs 并行                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   🍜 串行:厨师一个人,炒完炒饭再炒菜                             │
│      客户点4个菜 → 等20分钟 → 全部上齐                          │
│                                                                  │
│   👨‍🍳 并行:4个厨师同时炒菜                                     │
│      客户点4个菜 → 5分钟 → 全部上齐                             │
│                                                                  │
│   这就是并发的好处:充分利用 CPU,提高效率!                       │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

💡 实战场景

┌─────────────────────────────────────────────────────────────────┐
│                      高并发场景                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   🌐 双十一抢购                                               │
│      同一时刻上百万人下单 → 需要并发处理                         │
│                                                                  │
│   📱 抢红包                                                   │
│      群里有 500 人,每人每秒抢一次 → 高并发                      │
│                                                                  │
│   📊 数据统计                                                 │
│      报表系统需要在 1 分钟内统计千万条数据 → 并行计算            │
│                                                                  │
│   🔍 搜索建议                                                 │
│      每个人每秒触发多次联想词查询 → 并发读取                     │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

并发编程要解决的问题

  • 如何让多个任务同时执行?
  • 多个任务同时访问同一个资源怎么办?
  • 如何避免线程安全问题?

并发基础

进程与线程

┌─────────────────────────────────────────────────────────────────┐
│                      进程 vs 线程                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  进程 (Process)              线程 (Thread)                      │
│  ─────────────              ─────────────                      │
│  独立的内存空间              共享进程的内存空间                    │
│  操作系统分配资源的基本单位   CPU 调度的基本单位                   │
│  通信复杂 (IPC)              通信简单 (共享内存)                  │
│  创建/销毁开销大            创建/销毁开销小                      │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

创建线程的几种方式

java
// 方式1:继承 Thread
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("继承 Thread 创建线程");
    }
}
new MyThread().start();

// 方式2:实现 Runnable
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("实现 Runnable 创建线程");
    }
}
new Thread(new MyRunnable()).start();

// 方式3:实现 Callable + FutureTask (有返回值,可抛异常)
class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        return 123;
    }
}
FutureTask<Integer> future = new FutureTask<>(new MyCallable());
new Thread(future).start();
Integer result = future.get();  // 获取返回值

// 方式4:线程池 (推荐)
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(() -> System.out.println("线程池执行"));
executor.shutdown();

线程状态

                    ┌─────────────┐
                    │    NEW      │  创建,尚未启动
                    └──────┬──────┘


                    ┌─────────────┐
                    │  RUNNABLE   │  可运行,正在运行或等待CPU
                    └──────┬──────┘

            ┌──────────────┼──────────────┐
            │              │              │
            ▼              ▼              ▼
    ┌───────────┐  ┌────────────┐  ┌───────────┐
    │  BLOCKED  │  │  WAITING   │  │TIMED_WAITING│
    │  阻塞等待  │  │  无限等待   │  │  计时等待   │
    │  监视器锁  │  │ wait/join  │  │ sleep/poll │
    └─────┬─────┘  └─────┬──────┘  └─────┬──────┘
          │              │              │
          └──────────────┬┴──────────────┘

                  ┌─────────────┐
                  │ TERMINATED  │  线程执行完毕
                  └─────────────┘

线程状态转换示例

java
public class ThreadStateDemo {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            System.out.println("线程状态: " + Thread.currentThread().getState());
            // RUNNABLE
        });

        System.out.println("启动前: " + thread.getState()); // NEW
        thread.start();
        System.out.println("启动后: " + thread.getState());  // RUNNABLE

        Thread.sleep(100);
        System.out.println("等待中: " + thread.getState());  // RUNNABLE

        thread.join();
        System.out.println("结束后: " + thread.getState());  // TERMINATED
    }
}

synchronized 关键字

基本用法

java
// 1. 同步方法
public synchronized void method() {
    // 同步代码
}

// 2. 同步代码块
public void method() {
    synchronized (this) {  // 对象锁
        // 同步代码
    }
}

// 3. 静态同步方法 (锁的是 Class 对象)
public static synchronized void staticMethod() {
    // 同步代码
}

// 4. 同步类对象
public void method() {
    synchronized (MyClass.class) {  // 类锁
        // 同步代码
    }
}

锁的原理

java
public class SynchronizedDemo {

    // 对象锁示例
    public synchronized void method1() {
        // 相当于 synchronized(this)
        System.out.println("同步方法1");
    }

    public void method2() {
        synchronized (this) {
            System.out.println("同步代码块");
        }
    }

    // 类锁示例
    public static synchronized void staticMethod1() {
        // 相当于 synchronized(SynchronizedDemo.class)
        System.out.println("静态同步方法");
    }

    public static void staticMethod2() {
        synchronized (SynchronizedDemo.class) {
            System.out.println("类锁代码块");
        }
    }
}

💡 面试高频点:synchronized 锁的是什么?

┌─────────────────────────────────────────────────────────────────┐
│                    synchronized 锁的对象                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  实例方法 → this 对象(当前实例)                               │
│  静态方法 → Class 对象(MyClass.class)                         │
│  代码块 → 括号中的对象                                           │
│                                                                  │
│  所有 synchronized 实例方法共享同一把锁                            │
│  所有 synchronized 静态方法共享另一把锁                            │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Lock 接口

ReentrantLock

java
public class LockDemo {

    private final ReentrantLock lock = new ReentrantLock();

    public void method() {
        lock.lock();  // 获取锁
        try {
            // 同步代码
            System.out.println("执行同步操作");
        } finally {
            lock.unlock();  // 必须在 finally 中释放
        }
    }

    // 可重入示例
    public synchronized void methodA() {
        System.out.println("methodA");
        methodB();  // 可重入,同一把锁
    }

    public synchronized void methodB() {
        System.out.println("methodB");
    }
}

ReentrantLock vs synchronized

特性synchronizedReentrantLock
获取方式自动获取/释放手动获取/释放
尝试非阻塞获取不支持tryLock()
超时获取不支持tryLock(timeout)
中断等待不支持lockInterruptibly()
公平锁不支持可选公平/非公平
多条件Condition不支持newCondition()
java
// ReentrantLock 高级特性
ReentrantLock lock = new ReentrantLock();

// 1. 尝试获取锁(非阻塞)
if (lock.tryLock()) {
    try {
        // 获取到锁
    } finally {
        lock.unlock();
    }
} else {
    // 获取锁失败
}

// 2. 超时获取锁
try {
    if (lock.tryLock(5, TimeUnit.SECONDS)) {
        try {
            // 5秒内获取到锁
        } finally {
            lock.unlock();
        }
    }
} catch (InterruptedException e) {
    // 等待时被中断
}

// 3. 公平锁(先来先服务)
ReentrantLock fairLock = new ReentrantLock(true);

线程通信

wait/notify/notifyAll

java
public class WaitNotifyDemo {

    private final Object lock = new Object();
    private boolean ready = false;

    // 生产者
    public void produce() {
        synchronized (lock) {
            ready = true;
            lock.notify();  // 唤醒一个等待线程
            // lock.notifyAll();  // 唤醒所有等待线程
        }
    }

    // 消费者
    public void consume() {
        synchronized (lock) {
            while (!ready) {  // 必须用 while,防止虚假唤醒
                try {
                    lock.wait();  // 释放锁并等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 消费
            System.out.println("消费数据");
        }
    }
}

💡 面试高频点:为什么 wait/notify 必须在同步代码块中调用?

原因:wait/notify 需要获取对象监视器锁,才能操作对象的等待队列
如果在同步块外调用,会抛出 IllegalMonitorStateException

Condition 接口

java
public class ConditionDemo {

    private final ReentrantLock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    private boolean ready = false;

    public void awaitTest() throws InterruptedException {
        lock.lock();
        try {
            while (!ready) {
                condition.await();  // 等待
            }
            System.out.println("继续执行");
        } finally {
            lock.unlock();
        }
    }

    public void signalTest() {
        lock.lock();
        try {
            ready = true;
            condition.signal();  // 唤醒一个
            // condition.signalAll();  // 唤醒所有
        } finally {
            lock.unlock();
        }
    }
}

线程池

线程池架构

┌─────────────────────────────────────────────────────────────────┐
│                       线程池架构                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│                     Executor                                    │
│                        │                                        │
│                   ExecutorService                               │
│                        │                                        │
│    ┌───────────────────┼───────────────────┐                     │
│    │                   │                   │                     │
│ ThreadPoolExecutor  ScheduledExecutor    ForkJoinPool           │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

创建线程池

java
// 1. FixedThreadPool - 固定大小
ExecutorService fixedPool = Executors.newFixedThreadPool(5);

// 2. CachedThreadPool - 可缓存,按需创建
ExecutorService cachedPool = Executors.newCachedThreadPool();

// 3. SingleThreadExecutor - 单线程
ExecutorService singlePool = Executors.newSingleThreadExecutor();

// 4. ScheduledThreadPool - 定时任务
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(5);

// 5. ForkJoinPool - 分治任务 (Java 8+)
ForkJoinPool forkJoinPool = ForkJoinPool.common();

ThreadPoolExecutor

java
// 参数详解
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    int corePoolSize,      // 核心线程数
    int maximumPoolSize,   // 最大线程数
    long keepAliveTime,    // 空闲线程存活时间
    TimeUnit unit,         // 时间单位
    BlockingQueue<Runnable> workQueue,  // 任务队列
    ThreadFactory threadFactory,         // 线程工厂
    RejectedExecutionHandler handler     // 拒绝策略
);

// 拒绝策略
// 1. AbortPolicy - 抛出RejectedExecutionException (默认)
// 2. CallerRunsPolicy - 由调用者线程执行
// 3. DiscardPolicy - 丢弃任务
// 4. DiscardOldestPolicy - 丢弃队列最旧的任务

💡 面试高频点:线程池执行流程?

┌─────────────────────────────────────────────────────────────────┐
│                    线程池执行流程                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  1. 提交任务                                                     │
│            ↓                                                     │
│  2. 核心线程数未满 → 创建核心线程执行                              │
│            ↓                                                     │
│  3. 核心线程数已满 → 加入任务队列                                 │
│            ↓                                                     │
│  4. 队列已满 & 最大线程数未满 → 创建临时线程执行                   │
│            ↓                                                     │
│  5. 队列已满 & 最大线程数已满 → 执行拒绝策略                       │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

线程池使用示例

java
ExecutorService executor = Executors.newFixedThreadPool(5);

// 提交 Runnable
executor.submit(() -> {
    System.out.println("任务执行");
});

// 提交 Callable
Future<String> future = executor.submit(() -> "结果");
String result = future.get();  // 阻塞获取结果

// 关闭线程池
executor.shutdown();        // 不再接受新任务,等待已提交任务完成
// executor.shutdownNow();  // 尝试停止所有任务

// 等待所有任务完成
executor.awaitTermination(60, TimeUnit.SECONDS);

JUC 并发工具类

CountDownLatch

java
// 等待多个线程完成后主线程继续
public class CountDownLatchDemo {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(3);

        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                System.out.println("任务完成");
                latch.countDown();  // 计数减1
            }).start();
        }

        latch.await();  // 等待计数为0
        System.out.println("所有任务完成,主线程继续");
    }
}

CyclicBarrier

java
// 多个线程互相等待,到齐后一起执行
public class CyclicBarrierDemo {

    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(3, () -> {
            System.out.println("所有人都到了,开始执行!");
        });

        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                System.out.println("等待中...");
                barrier.await();  // 等待其他线程
                System.out.println("继续执行");
            }).start();
        }
    }
}

Semaphore

java
// 控制并发访问资源的线程数
public class SemaphoreDemo {

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(2);  // 同时只允许2个线程

        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire();  // 获取许可证
                    System.out.println("执行任务");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();  // 释放许可证
                }
            }).start();
        }
    }
}

Exchanger

java
// 两个线程交换数据
public class ExchangerDemo {

    public static void main(String[] args) {
        Exchanger<String> exchanger = new Exchanger<>();

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

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

原子类

AtomicInteger 等

java
// 原子操作,无锁实现线程安全
public class AtomicDemo {

    private AtomicInteger counter = new AtomicInteger(0);

    public void increment() {
        counter.incrementAndGet();  // 自增并返回
        // counter.getAndIncrement();  // 返回后自增
        // counter.decrementAndGet();  // 自减并返回
    }

    public int get() {
        return counter.get();
    }
}

// 常用方法
atomicInt.incrementAndGet();     // ++i
atomicInt.getAndIncrement();     // i++
atomicInt.addAndGet(5);         // i += 5
atomicInt.compareAndSet(0, 1);  // CAS 操作

AtomicReference

java
// 原子引用,用于对象
public class AtomicRefDemo {

    private AtomicReference<User> userRef = new AtomicReference<>();

    public void update(User newUser) {
        userRef.compareAndSet(
            userRef.get(),  // 期望值
            newUser         // 新值
        );
    }
}

并发容器

ConcurrentHashMap

java
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

// 常用操作
map.putIfAbsent("key", 1);  // 不存在才插入
map.computeIfAbsent("key", k -> 1);  // 不存在才计算插入
map.merge("key", 1, Integer::sum);  // 合并操作

// 原子操作
map.getAndAdd("key", 10);  // 原子加减
map.updateAndGet("key", v -> v * 2);  // 原子更新

// 批量操作 (Java 8+)
map.forEach((k, v) -> System.out.println(k + ": " + v));

CopyOnWriteArrayList

java
// 读写分离,写时复制
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

list.add("a");      // 每次都复制整个数组
list.addIfAbsent("b");  // 不存在才添加

BlockingQueue

java
// 阻塞队列,生产者-消费者模式
BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(10);

// 生产者
new Thread(() -> {
    try {
        queue.put(1);  // 队列满则阻塞
        queue.offer(1, 5, TimeUnit.SECONDS);  // 超时返回false
    } catch (InterruptedException e) { }
}).start();

// 消费者
new Thread(() -> {
    try {
        Integer item = queue.take();  // 队列空则阻塞
        Integer item2 = queue.poll(5, TimeUnit.SECONDS);  // 超时返回null
    } catch (InterruptedException e) { }
}).start();

总结

并发编程核心知识点

知识点面试频率实战重要性
线程创建方式⭐⭐⭐⭐⭐⭐⭐⭐⭐
synchronized 原理⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
synchronized vs Lock⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
wait/notify 原理⭐⭐⭐⭐⭐⭐⭐⭐
线程池原理⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
JUC 工具类⭐⭐⭐⭐⭐⭐⭐⭐
线程安全集合⭐⭐⭐⭐⭐⭐⭐⭐⭐

⚠️ 易错点提醒

  1. wait() 会释放锁,sleep() 不会释放锁
  2. notify() 随机唤醒一个,notifyAll() 唤醒所有
  3. 使用 wait()/notify() 必须用 while 判断条件
  4. 线程池拒绝策略在队列满且线程数达到最大值时触发
  5. volatile 只能保证可见性,不能保证原子性