【JavaSE全面教学】Java多线程与并发基础Day15(2026年)
写在前面:这是JavaSE系列的最后一篇!多线程是Java进阶的必经之路,也是面试中区分初级和中高级开发者的分水岭。今天我们讲多线程的基础知识,包括线程的创建、生命周期、同步机制和线程池。

一、进程与线程
1.1 基本概念
进程(Process):
- 操作系统分配资源的基本单位
- 每个进程有独立的内存空间
- 例如:打开一个浏览器就是一个进程
线程(Thread):
- CPU调度的基本单位
- 一个进程可以包含多个线程
- 线程共享进程的内存空间
- 例如:浏览器中同时下载多个文件,每个下载任务是一个线程
1.2 为什么需要多线程?
实际场景:想象一下你在浏览器里同时下载多个文件。如果是单线程,必须等第一个文件下载完才能开始第二个。而多线程可以让多个下载任务同时进行,大大缩短总时间。
// 单线程:任务串行执行
// 任务1(3秒)→ 任务2(2秒)→ 任务3(5秒)= 总共10秒
// 多线程:任务并行执行
// 任务1(3秒)
// 任务2(2秒) → 总共5秒(取决于最慢的任务)
// 任务3(5秒)
// 好处:
// 1. 提高CPU利用率
// 2. 提高程序响应速度
// 3. 充分利用多核CPU
经验之谈:在实际项目中,多线程最常见的应用场景是:批量数据处理、异步任务执行、定时任务调度。但也要注意,线程不是越多越好,过多的线程会导致上下文切换开销增大,反而降低性能。
二、创建线程的四种方式
2.1 继承Thread类
// 方式1:继承Thread类,重写run方法
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程:" + i);
}
}
}
// 启动线程
public class Test {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start(); // 启动线程(不是run!)
for (int i = 0; i < 5; i++) {
System.out.println("主线程:" + i);
}
}
}
// 注意:调用start()才是启动线程
// 调用run()只是普通方法调用,还是在主线程执行
踩坑提醒:新手最容易犯的错误就是调用run()而不是start()。调用run()不会创建新线程,只是在当前线程执行方法体,完全达不到多线程的效果。
2.2 实现Runnable接口(推荐)
// 方式2:实现Runnable接口
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("子线程:" + i);
}
}
}
// 启动线程
public class Test {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start();
}
}
// 方式3:Lambda表达式(Java 8+,最简洁)
new Thread(() -> {
System.out.println("子线程运行");
}).start();
2.3 实现Callable接口(有返回值)
import java.util.concurrent.*;
// 方式4:实现Callable接口(可以返回结果)
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
return sum; // 返回结果
}
}
public class Test {
public static void main(String[] args) throws Exception {
// 创建FutureTask
FutureTask<Integer> task = new FutureTask<>(new MyCallable());
// 启动线程
new Thread(task).start();
// 获取结果(会阻塞直到线程执行完毕)
Integer result = task.get();
System.out.println("1到100的和:" + result); // 5050
}
}
2.4 线程池(生产环境推荐)
经验之谈:在实际项目中,千万不要用new Thread()创建线程!线程创建和销毁是有开销的,而且线程数不可控可能导致OOM。线程池可以复用线程、控制并发数,是生产环境的标准做法。
import java.util.concurrent.*;
// 创建线程池
ExecutorService pool = Executors.newFixedThreadPool(3); // 3个线程的固定线程池
// 提交任务
pool.execute(() -> System.out.println("任务1"));
pool.execute(() -> System.out.println("任务2"));
pool.execute(() -> System.out.println("任务3"));
// 关闭线程池
pool.shutdown();
2.5 四种方式对比
| 方式 | 优点 | 缺点 | 推荐度 |
|---|---|---|---|
| 继承Thread | 简单直接 | Java单继承限制 | ★★☆☆☆ |
| 实现Runnable | 灵活,可继承其他类 | 无返回值 | ★★★★☆ |
| 实现Callable | 有返回值 | 稍复杂 | ★★★★☆ |
| 线程池 | 复用线程,性能好 | 需要理解线程池原理 | ★★★★★ |
三、线程的生命周期
3.1 六种状态
NEW(新建)
↓ start()
RUNNABLE(可运行)
↓ 获得CPU时间片
↓ 等待锁 / sleep / join / IO
BLOCKED(阻塞) ← 获得锁 → RUNNABLE
WAITING(等待) ← notify / notifyAll → RUNNABLE
TIMED_WAITING(超时等待) ← 超时结束 → RUNNABLE
↓ run()执行完毕
TERMINATED(终止)
3.2 状态转换示例
Thread t = new Thread(() -> {
try {
Thread.sleep(1000); // TIMED_WAITING
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// t的状态:NEW
t.start();
// t的状态:RUNNABLE
// t执行完毕后
// t的状态:TERMINATED
四、线程同步
4.1 为什么需要同步?
踩坑提醒:多线程操作共享数据时,不加同步机制会导致数据不一致。我曾经遇到过一个Bug:多个线程同时修改库存数量,结果出现超卖。这就是典型的竞态条件问题。
// 不加同步的例子:多个线程同时操作共享数据
class Counter {
private int count = 0;
public void increment() {
count++; // 不是原子操作!
}
}
// count++ 实际上是3步操作:
// 1. 读取count的值
// 2. count + 1
// 3. 写回count
// 如果两个线程同时执行count++,可能出现:
// 线程A读取count=0 → 线程B读取count=0
// 线程A写入count=1 → 线程B写入count=1
// 结果应该是2,实际是1(数据不一致)
4.2 synchronized关键字
// 方式1:同步代码块
class Counter {
private int count = 0;
private final Object lock = new Object();
public void increment() {
synchronized (lock) { // 锁住对象
count++;
}
}
}
// 方式2:同步方法
class Counter {
private int count = 0;
public synchronized void increment() { // 锁住this
count++;
}
}
// 方式3:同步静态方法
class Counter {
private static int count = 0;
public static synchronized void increment() { // 锁住Class对象
count++;
}
}
4.3 Lock接口(更灵活)
经验之谈:Lock比synchronized更灵活,可以实现公平锁、可中断锁、超时获取锁等。但使用Lock时一定要在finally中释放锁,否则一旦出现异常,锁就永远释放了,其他线程会一直阻塞。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Counter {
private int count = 0;
private final Lock lock = new ReentrantLock();
public void increment() {
lock.lock(); // 加锁
try {
count++;
} finally {
lock.unlock(); // 一定要在finally中释放锁
}
}
}
踩坑提醒:忘记在finally中unlock是Lock使用的常见错误。相比之下synchronized会自动释放锁,更安全一些。
4.4 volatile关键字
// volatile:保证可见性,不保证原子性
class VolatileDemo {
private volatile boolean running = true;
public void stop() {
running = false; // 一个线程修改
}
public void run() {
while (running) { // 另一个线程能立即看到变化
// 执行任务
}
}
}
// volatile vs synchronized
// volatile:保证可见性,不保证原子性
// synchronized:保证可见性和原子性
五、线程通信
5.1 wait和notify
class SharedResource {
private int count = 0;
// 生产者
public synchronized void produce() throws InterruptedException {
while (count >= 10) {
wait(); // 等待(释放锁)
}
count++;
System.out.println("生产:" + count);
notifyAll(); // 通知消费者
}
// 消费者
public synchronized void consume() throws InterruptedException {
while (count <= 0) {
wait(); // 等待
}
count--;
System.out.println("消费:" + count);
notifyAll(); // 通知生产者
}
}
5.2 sleep和wait的区别
| 特性 | sleep | wait |
|---|---|---|
| 所属类 | Thread | Object |
| 释放锁 | 不释放 | 释放 |
| 使用位置 | 任何地方 | synchronized块中 |
| 唤醒方式 | 自动超时 | notify/notifyAll |
六、线程池
6.1 为什么需要线程池?
// 不用线程池:
// 每次执行任务都创建新线程
// 创建和销毁线程开销大
// 线程数量不可控,可能OOM
// 用线程池:
// 复用线程,减少创建销毁开销
// 控制最大并发数
// 提供任务队列和拒绝策略
6.2 创建线程池
import java.util.concurrent.*;
// 1. 固定大小线程池
ExecutorService pool1 = Executors.newFixedThreadPool(5);
// 5个线程,任务队列无界
// 2. 缓存线程池
ExecutorService pool2 = Executors.newCachedThreadPool();
// 线程数量不固定,按需创建
// 3. 单线程池
ExecutorService pool3 = Executors.newSingleThreadExecutor();
// 只有1个线程,保证任务按顺序执行
// 4. 定时任务线程池
ScheduledExecutorService pool4 = Executors.newScheduledThreadPool(3);
pool4.schedule(() -> System.out.println("3秒后执行"), 3, TimeUnit.SECONDS);
pool4.scheduleAtFixedRate(() -> System.out.println("定时执行"), 0, 1, TimeUnit.SECONDS);
6.3 ThreadPoolExecutor(推荐)
踩坑提醒:阿里巴巴Java开发手册明确规定,生产环境禁止使用Executors的快捷方法创建线程池!因为newFixedThreadPool和newSingleThreadExecutor使用的是无界队列,任务堆积会导致OOM;newCachedThreadPool允许无限创建线程,也会导致OOM。
// 生产环境推荐手动创建线程池(不用Executors工厂方法)
ThreadPoolExecutor pool = new ThreadPoolExecutor(
2, // 核心线程数
5, // 最大线程数
60L, // 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new ArrayBlockingQueue<>(100), // 任务队列(有界队列)
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
// 提交任务
pool.execute(() -> System.out.println("任务1")); // 无返回值
Future<Integer> future = pool.submit(() -> { // 有返回值
return 42;
});
Integer result = future.get(); // 获取结果
// 关闭线程池
pool.shutdown(); // 不接受新任务,等待已提交的任务完成
6.4 拒绝策略
| 策略 | 说明 |
|---|---|
| AbortPolicy | 抛出RejectedExecutionException(默认) |
| CallerRunsPolicy | 由提交任务的线程执行 |
| DiscardPolicy | 直接丢弃任务 |
| DiscardOldestPolicy | 丢弃队列中最老的任务 |
参考资料
七、面试高频考点
考点1:start和run的区别
// start():启动新线程,在子线程中执行run()
// run():普通方法调用,在当前线程中执行
追问:多次调用start会怎样?
答案:会抛出IllegalThreadStateException。线程一旦启动,就不能再次启动。
考点2:synchronized和Lock的区别
| 特性 | synchronized | Lock |
|---|---|---|
| 锁释放 | 自动释放 | 手动unlock |
| 中断 | 不可中断 | 可以中断 |
| 公平性 | 非公平 | 可选公平 |
| 条件绑定 | 一个锁一个条件 | 一个锁多个条件 |
延伸:什么情况下用Lock不用synchronized?
答案:需要公平锁、可中断锁、超时获取锁、多个条件变量时,用Lock更灵活。
考点3:线程池的核心参数
// corePoolSize:核心线程数
// maximumPoolSize:最大线程数
// keepAliveTime:空闲线程存活时间
// workQueue:任务队列
// handler:拒绝策略
追问:线程池的执行流程是怎样的?
答案:提交任务→核心线程是否已满?否:创建核心线程执行任务;是:加入队列;队列已满?否:加入队列;是:创建非核心线程;非核心线程数达上限?否:创建非核心线程;是:执行拒绝策略。
八、总结
今天我们学习了:
- ✅ 进程和线程的概念
- ✅ 创建线程的四种方式
- ✅ 线程的生命周期
- ✅ 线程同步(synchronized、Lock、volatile)
- ✅ 线程通信(wait/notify)
- ✅ 线程池的使用
重点记忆:
- 推荐用Runnable或线程池创建线程
- start()启动线程,run()只是普通调用
- synchronized保证原子性和可见性
- volatile只保证可见性
- 生产环境用手动创建的ThreadPoolExecutor
🎉 JavaSE全面教学系列完结
恭喜你学完了JavaSE全面教学系列的全部15篇文章!
学习路线回顾:
Week 1(Day1-5):基础语法 ✅
Week 2(Day6-10):面向对象 ✅
Week 3(Day11-15):进阶特性 ✅
下一步建议:
- 复习本系列所有文章
- 把每篇的代码都自己敲一遍
- 刷LeetCode巩固语法
- 学习JavaWeb/框架(Spring Boot等)
- 做一个完整的项目
互动话题:恭喜你完成了JavaSE全面教学系列!你学完之后有什么收获或困惑?欢迎在评论区分享!
如果这个系列对你有帮助,欢迎点赞、收藏、关注三连支持!后续我会更新Java进阶系列,关注我不迷路 👇
本文为【JavaSE全面教学】系列第15篇(完结),感谢你的学习!
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)