Skip to content

Java 多线程编程

创建时间: 2026-04-15 标签: #Java #多线程 #并发编程 #面试重点


1. 线程基础概念

什么是线程

  • 进程(Process):程序的一次执行实例,拥有独立的内存空间
  • 线程(Thread):进程内的执行单元,共享进程的内存空间,轻量级

线程 vs 进程

特性进程线程
资源占用
创建/销毁
通信复杂(IPC)简单(共享内存)
独立性独立共享进程资源

2. 线程创建方式

方式一:继承 Thread 类

java
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程执行中: " + Thread.currentThread().getName());
    }
}

// 使用
MyThread t = new MyThread();
t.start();  // 启动线程(不是 run())

方式二:实现 Runnable 接口(推荐)

java
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable 线程执行中");
    }
}

// 使用
Thread t = new Thread(new MyRunnable());
t.start();

方式三:实现 Callable 接口(带返回值)

java
class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        return 42;
    }
}

// 使用
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(new MyCallable());
Integer result = future.get();  // 阻塞等待结果
executor.shutdown();

方式四:线程池(生产环境推荐)

java
ExecutorService executor = Executors.newFixedThreadPool(4);
executor.submit(() -> {
    // 任务逻辑
    System.out.println("线程池任务");
});
executor.shutdown();

3. 线程生命周期

┌─────────────────────────────────────────┐
│                 NEW                      │
│      (创建但未启动)                        │
└─────────────┬───────────────────────────┘
              │ start()

┌─────────────────────────────────────────┐
│            RUNNABLE                     │
│   (可运行,可能正在运行也可能等待CPU)       │
└─────────────┬───────────────────────────┘

  ┌───────────┼───────────┐
  │           │           │
  ▼           ▼           ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ RUNNING  │ │ BLOCKED  │ │ WAITING  │
│ (运行中)  │ │ (阻塞)   │ │ (等待)   │
└────┬─────┘ └──────────┘ └──────────┘
     │              ▲           ▲
     │              │           │
     └──────────────┴───────────┘


┌─────────────────────────────────────────┐
│            TERMINATED                    │
│              (结束)                       │
└─────────────────────────────────────────┘

状态说明

状态说明
NEW线程对象创建,未调用 start()
RUNNABLE调用 start() 后的就绪/运行状态
BLOCKEDsynchronized 锁等待
WAITINGwait(), join(), LockSupport.park() 等待
TIMED_WAITING带超时的等待(sleep, join(timeout))
TERMINATED线程执行完毕

4. 线程同步机制

4.1 synchronized 关键字

java
// 同步方法
public synchronized void method() {
    // 线程安全的操作
}

// 同步代码块
public void method() {
    synchronized (this) {  // 锁对象
        // 临界区代码
    }
}

// 静态同步方法(锁类对象)
public static synchronized void staticMethod() {
    // 锁住的是 Class 对象
}

4.2 Lock 接口(显式锁)

java
private final ReentrantLock lock = new ReentrantLock();

public void method() {
    lock.lock();
    try {
        // 临界区
    } finally {
        lock.unlock();  // 必须在 finally 中释放
    }
}

synchronized vs Lock

特性synchronizedLock
获得锁自动释放手动释放
尝试非阻塞获取✅ tryLock()
超时获取✅ tryLock(timeout)
公平锁✅ ReentrantLock(true)
条件等待wait/notifyCondition

4.3 volatile 关键字

java
private volatile boolean flag = false;

作用

  • 保证可见性:修改对所有线程立即可见
  • 禁止指令重排序

⚠️ 注意:volatile 不保证原子性!

4.4 ThreadLocal 线程本地变量

java
private static final ThreadLocal<SimpleDateFormat> formatter = 
    ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd"));

// 每个线程独立的对象
String result = formatter.get();

5. 线程间通信

5.1 wait/notify/notifyAll

java
public class SharedResource {
    private boolean available = false;

    public synchronized void produce() {
        while (available) {
            try {
                wait();  // 等待消费者消费
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        // 生产
        available = true;
        notify();  // 通知消费者
    }

    public synchronized void consume() {
        while (!available) {
            try {
                wait();  // 等待生产者生产
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        // 消费
        available = false;
        notify();  // 通知生产者
    }
}

5.2 Condition 条件变量

java
private final ReentrantLock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();

public void put(Object item) {
    lock.lock();
    try {
        while (isFull()) {
            notFull.await();
        }
        // 添加元素
        notEmpty.signal();
    } finally {
        lock.unlock();
    }
}

5.3 BlockingQueue(推荐的生产者-消费者模式)

java
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(100);

// 生产者
queue.put(1);  // 阻塞直到队列有空位

// 消费者
Integer item = queue.take();  // 阻塞直到队列有元素

6. 线程池

6.1 常用线程池类型

java
// 固定大小线程池
ExecutorService fixed = Executors.newFixedThreadPool(10);

// 缓存线程池(自动扩容)
ExecutorService cached = Executors.newCachedThreadPool();

// 单线程线程池
ExecutorService single = Executors.newSingleThreadExecutor();

// 调度线程池
ScheduledExecutorService scheduled = Executors.newScheduledThreadPool(5);

⚠️ 阿里巴巴规范警告Executors 返回的线程池存在资源耗尽风险,推荐使用 ThreadPoolExecutor 手动创建。

6.2 ThreadPoolExecutor 详解

java
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    2,                      // corePoolSize:核心线程数
    10,                     // maximumPoolSize:最大线程数
    60L,                    // keepAliveTime:空闲线程存活时间
    TimeUnit.SECONDS,       // 时间单位
    new LinkedBlockingQueue<>(100),  // 任务队列
    new ThreadFactory() {    // 线程工厂
        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            t.setName("my-pool-" + t.getName());
            return t;
        }
    },
    new ThreadPoolExecutor.AbortPolicy()  // 拒绝策略
);

6.3 拒绝策略

策略行为
AbortPolicy抛出 RejectedExecutionException
CallerRunsPolicy由调用线程执行任务
DiscardPolicy静默丢弃
DiscardOldestPolicy丢弃队列中最旧的任务

7. 高级并发工具

7.1 CountDownLatch(倒计时门闩)

java
CountDownLatch latch = new CountDownLatch(3);

for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        // 执行任务
        latch.countDown();  // 计数减一
    }).start();
}

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

场景:多线程任务汇总、主线程等待子任务完成

7.2 CyclicBarrier(循环栅栏)

java
CyclicBarrier barrier = new CyclicBarrier(3, () -> 
    System.out.println("所有线程到达屏障,执行汇总操作")
);

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

场景:多线程分阶段任务、并发测试

7.3 Semaphore(信号量)

java
Semaphore semaphore = new Semaphore(3);  // 3个许可

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

场景:限流、连接池、资源访问控制

7.4 Phaser(阶段同步器)

java
Phaser phaser = new Phaser(3);  // 3个参与者

for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        // 阶段1
        phaser.arriveAndAwaitAdvance();
        // 阶段2
        phaser.arriveAndAwaitAdvance();
    }).start();
}

场景:多阶段并行任务


8. 并发集合

java
// 线程安全的 List
List<String> list = new CopyOnWriteArrayList<>();
// 或 Collections.synchronizedList(new ArrayList<>());

// 线程安全的 Map
Map<String, Object> map = new ConcurrentHashMap<>();

// 线程安全的 Set
Set<String> set = new CopyOnWriteArraySet<>();
// 或 Collections.synchronizedSet(new HashSet<>());

// 高性能并发队列
Queue<Integer> queue = new ConcurrentLinkedQueue<>();
BlockingQueue<Integer> bq = new LinkedBlockingQueue<>();

并发集合对比

集合特点适用场景
ConcurrentHashMap分段锁/无锁高并发读写的 Map
CopyOnWriteArrayList写时复制读多写少的 List
ConcurrentLinkedQueue无锁 CAS高并发队列
BlockingQueue支持阻塞操作生产者-消费者

9. 原子类

java
// 原子整数
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet();  // ++i
counter.getAndIncrement();  // i++
counter.addAndGet(5);       // i += 5

// 原子引用
AtomicReference<User> userRef = new AtomicReference<>();
userRef.compareAndSet(oldUser, newUser);

// 原子累加器(高并发,性能优于 AtomicInteger)
LongAdder sum = new LongAdder();
sum.add(100);
long result = sum.sum();

10. 最佳实践

✅ 推荐做法

java
// 1. 优先使用线程池,不直接创建线程
ExecutorService executor = new ThreadPoolExecutor(
    Runtime.getRuntime().availableProcessors(),  // CPU核心数
    100,
    60L, TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(1000),
    new ThreadPoolExecutor.CallerRunsPolicy()
);

// 2. 使用 volatile 保证可见性
private volatile boolean shutdown = false;

// 3. 优先使用并发工具类
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
BlockingQueue<Task> queue = new LinkedBlockingQueue<>(100);

// 4. 使用 atomic 类
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet();

// 5. 异常处理
executor.submit(() -> {
    try {
        // 任务逻辑
    } catch (Exception e) {
        log.error("任务执行失败", e);
    }
});

❌ 避免做法

java
// 1. 不要直接创建线程
// 错误 ❌
new Thread(() -> doWork()).start();

// 2. 不要在锁内执行耗时操作
synchronized (lock) {
    Thread.sleep(1000);  // 错误:持有锁时睡眠
}

// 3. 不要忽略中断
try {
    while (condition) {
        // 处理
    }
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();  // 正确处理
}

11. 常见面试问题

Q1: synchronized 和 ReentrantLock 区别?

对比synchronizedReentrantLock
来源JVM 内置JDK 5+
释放自动释放手动释放
公平锁
尝试获取锁✅ tryLock()
超时获取✅ tryLock(timeout)
可中断✅ lockInterruptibly()
条件变量wait/notifyCondition

Q2: volatile 和 synchronized 区别?

特性volatilesynchronized
可见性
原子性
有序性禁止重排序保证
性能轻量重量

Q3: 线程死锁如何避免?

  1. 避免嵌套锁:尽量减少锁的嵌套
  2. 固定加锁顺序:多把锁按固定顺序获取
  3. 使用 tryLock 带超时:避免无限等待
  4. 减少锁粒度:使用分段锁
  5. 使用并发工具:ConcurrentHashMap 等

Q4: 生产者-消费者如何实现?

java
// 方式1:BlockingQueue(推荐)
BlockingQueue<Item> queue = new LinkedBlockingQueue<>(100);

// 方式2:wait/notify
synchronized (queue) {
    while (queue.isEmpty()) {
        queue.wait();
    }
    Item item = queue.remove();
    queue.notifyAll();
}

Q5: ThreadLocal 内存泄漏原因?

ThreadLocalMap 中的 Entry 继承 WeakReference<ThreadLocal<?>>,key 是弱引用。但 value 是强引用,如果线程不结束或未调用 remove(),可能导致内存泄漏。

正确做法

  • 使用完手动 remove()
  • 使用完置为 null

12. 相关资料

  • **Java IO流详解**
  • **Java时间类详解**
  • **Java包装类详解**

📝 本文档使用 AI 辅助生成,内容基于 Java 多线程编程核心知识点整理。

Knowledge4J — Java 知识库