多线程是 Java 后端开发的核心技能,也是面试中的高频考点。很多初学者觉得多线程难,其实掌握了核心概念和常用工具后,并不复杂。

一、创建线程的三种方式

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

// 2. 实现 Runnable 接口(推荐)
class MyTask implements Runnable {
    @Override
    public void run() {
        System.out.println("任务执行中");
    }
}
new Thread(new MyTask()).start();

// 3. 实现 Callable 接口(带返回值)
class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "任务执行完毕";
    }
}
FutureTask<String> ft = new FutureTask<>(new MyCallable());
new Thread(ft).start();
String result = ft.get();  // 获取返回值

推荐优先使用 Runnable 或 Callable,比继承 Thread 更灵活,也能继续继承其他类。

二、线程生命周期

新建(NEW)→ 就绪(RUNNABLE)→ 运行(RUNNING)→ 阻塞(BLOCKED/WAITING)→ 死亡(TERMINATED)
Thread t = new Thread(() -> {
    System.out.println("运行中");
});
System.out.println(t.getState());  // NEW
t.start();
System.out.println(t.getState());  // RUNNABLE
Thread.sleep(100);
System.out.println(t.getState());  // TERMINATED

三、线程安全问题

多个线程同时访问共享数据,就会产生线程安全问题:

// ❌ 线程不安全的例子
class Counter {
    private int count = 0;
    public void increment() { count++; }  // 不是原子操作!
    public int getCount() { return count; }
}

// 测试:1000个线程各加1,结果很可能小于1000

解决方案:

// ✅ 方式一:synchronized 同步锁
class SafeCounter {
    private int count = 0;
    public synchronized void increment() { count++; }
    // 或者使用同步块
    public void increment() {
        synchronized (this) { count++; }
    }
}

// ✅ 方式二:Lock 锁
class LockCounter {
    private int count = 0;
    private final Lock lock = new ReentrantLock();
    public void increment() {
        lock.lock();
        try { count++; }
        finally { lock.unlock(); }
    }
}

// ✅ 方式三:原子类(推荐,性能最好)
class AtomicCounter {
    private AtomicInteger count = new AtomicInteger(0);
    public void increment() { count.incrementAndGet(); }
}

四、线程池——生产中禁止手动 new Thread

// 创建线程池的两种方式

// 1. 通过 Executors 工具类(不推荐,有坑)
ExecutorService pool1 = Executors.newFixedThreadPool(5);   // 队列无界,可能OOM
ExecutorService pool2 = Executors.newCachedThreadPool();   // 线程无界,可能OOM

// 2. 直接 new ThreadPoolExecutor(推荐)
ExecutorService pool = new ThreadPoolExecutor(
    2,                // corePoolSize:核心线程数
    5,                // maximumPoolSize:最大线程数
    60L,              // keepAliveTime:空闲线程存活时间
    TimeUnit.SECONDS, // 时间单位
    new LinkedBlockingQueue<>(10),  // 阻塞队列
    Executors.defaultThreadFactory(), // 线程工厂
    new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);

// 使用
pool.execute(() -> System.out.println("执行任务"));
Future<String> result = pool.submit(() -> "任务结果");

// 关闭
pool.shutdown();  // 不再接收新任务,已提交的继续执行

线程池的7个核心参数:

参数 作用
corePoolSize 核心线程数(常驻)
maximumPoolSize 最大线程数
keepAliveTime 非核心线程空闲最大存活时间
unit 时间单位
workQueue 任务队列(存等待的任务)
threadFactory 线程工厂(可自定义线程名)
handler 拒绝策略(队列+线程池都满了怎么办)

拒绝策略:

  • AbortPolicy:抛异常(默认)
  • CallerRunsPolicy:调用者线程自己执行
  • DiscardPolicy:直接丢掉,不报错
  • DiscardOldestPolicy:丢掉队首最旧任务

五、常用并发工具类

1. CountDownLatch——等所有线程完成

CountDownLatch latch = new CountDownLatch(3);

for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        System.out.println(Thread.currentThread().getName() + " 执行完毕");
        latch.countDown();  // 计数器减1
    }).start();
}

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

2. CyclicBarrier——线程互相等待

CyclicBarrier barrier = new CyclicBarrier(3, () -> {
    System.out.println("所有人都到了,开始开会!");
});

for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        System.out.println(Thread.currentThread().getName() + " 到达会议室");
        barrier.await();  // 等待其他人
    }).start();
}

3. Semaphore——限流

Semaphore semaphore = new Semaphore(3);  // 最多3个线程同时访问

for (int i = 0; i < 10; i++) {
    new Thread(() -> {
        try {
            semaphore.acquire();
            System.out.println(Thread.currentThread().getName() + " 开始处理");
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + " 处理完毕");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semaphore.release();  // 释放许可证
        }
    }).start();
}

六、volatile 关键字

// volatile 保证可见性,不保证原子性
public class Flag {
    private volatile boolean running = true;

    public void stop() { running = false; }

    public void run() {
        while (running) {
            // 没有 volatile,另一个线程改了 running,这里可能看不到
        }
    }
}

volatile 适用场景: 一个线程写、多个线程读的标记位变量。

七、实际开发建议

// 企业级项目中的线程池配置示例
@Configuration
public class ThreadPoolConfig {

    @Bean("taskExecutor")
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(100);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("async-task-");
        // 拒绝策略:由调用者线程执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

// 使用 @Async 异步执行
@Service
public class AsyncService {
    @Async("taskExecutor")
    public void sendNotification(Long userId) {
        // 异步发送通知,不阻塞主流程
        System.out.println("异步发送通知给用户: " + userId);
    }
}

总结

多线程开发的核心要点就四个:

线程安全 → 加锁或原子类
线程管理 → 用线程池,别 new Thread()
线程协作 → CountDownLatch / CyclicBarrier / Semaphore
标记共享 → volatile

掌握了这些,日常开发中的并发场景基本都能应付。


💡 觉得有用的话,点赞 + 关注【张老师技术栈】吧!每周更新 Java/Python/爬虫 实战干货,不让你白来。

更多推荐