前面14篇,我们搞定了:基础语法、面向对象、集合、IO流。
从今天开始,正式进入Java进阶核心难点------多线程。
多线程是Java进阶的分水岭,也是面试必考、项目必用的核心技术。
没有多线程,就没有高并发、没有秒杀系统、没有后台高性能服务。
很多新手学不懂多线程,是因为教程太抽象。本篇我用大白话+生活类比+极简代码,从零带你吃透多线程基础,零基础也能一次性看懂。

本篇核心学习目标:
-
彻底分清进程和线程的区别
-
掌握线程三大创建方式(面试必考)
-
看懂线程优先级、守护线程
-
吃透线程完整生命周期(5大状态)
-
理解多线程安全问题产生的根本原因
-
掌握最简单的线程安全解决方案
-
搞定多线程入门所有面试题
一、进程与线程(通俗白话理解)
1.1 什么是进程?
进程:正在运行的软件程序,是系统资源分配的最小单位。
生活举例:打开微信、打开浏览器、打开IDEA,每一个运行的软件都是一个进程。
进程特点:独立内存、相互隔离、互不干扰。
1.2 什么是线程?
线程:进程中的执行单元,是CPU调度的最小单位。
生活举例:
打开微信(进程),里面可以同时:聊天、刷朋友圈、接收消息、文件传输,每一个功能都是一个线程。
1.3 进程与线程核心区别(必背面试题)
| 对比维度 | 进程 | 线程 |
|---|---|---|
| 定义 | 程序运行实例,资源分配最小单位 | 进程执行单元,CPU调度最小单位 |
| 内存 | 独立内存,进程间隔离 | 共享进程内存,资源共享 |
| 开销 | 开销大、创建销毁慢 | 开销小、效率高 |
| 关系 | 一个进程至少包含1个线程 | 线程依赖进程存在 |
| 稳定性 | 一个进程崩溃不影响其他进程 | 一个线程崩溃,整个进程可能崩溃 |
核心总结:进程是大房子,线程是房间里的打工人;房子独立,工人共享资源。
二、线程三大创建方式(工作+面试必掌握)
2.1 方式一:继承Thread类(最简单)
步骤:
-
自定义类继承 Thread
-
重写 run() 方法(线程执行任务)
-
创建子类对象,调用 start() 启动线程
java
// 1. 自定义线程类
public class MyThread extends Thread{
@Override
public void run() {
// 线程执行的任务
for (int i = 0; i < 10; i++) {
System.out.println("线程执行:" + i);
}
}
}
// 测试类
public class ThreadDemo {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start(); // 启动线程
}
}
注意:必须调用start(),不是run()!直接调用run()只是普通方法,不是多线程!
2.2 方式二:实现Runnable接口(推荐、解耦)
Java单继承多实现,这种方式避免单继承限制,任务和线程分离,更灵活。
java
// 1. 实现任务接口
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("Runnable线程:" + i);
}
}
}
// 测试
public class ThreadDemo {
public static void main(String[] args) {
// 任务对象
MyRunnable run = new MyRunnable();
// 线程对象 + 任务对象
Thread t = new Thread(run);
t.start();
}
}
2.3 方式三:Callable接口(有返回值、可抛异常)
前两种无返回值、不能捕获任务异常,Callable可以获取线程执行结果,多用于并发任务获取结果场景。
java
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
// 带返回值的线程任务
public 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 ThreadDemo {
public static void main(String[] args) throws Exception {
MyCallable call = new MyCallable();
FutureTask<Integer> task = new FutureTask<>(call);
Thread t = new Thread(task);
t.start();
// 获取线程返回结果
System.out.println("结果:" + task.get());
}
}
2.4 三种创建方式对比(面试必背)
| 创建方式 | 优点 | 缺点 | 场景 |
|---|---|---|---|
| 继承Thread | 写法简单 | 单继承限制,无法继承其他类 | 简单临时线程 |
| 实现Runnable | 解耦、灵活、共享任务 | 无返回值 | 日常开发首选 |
| 实现Callable | 有返回值、可抛异常 | 写法复杂 | 需要获取线程结果 |
三、线程常用基础方法
java
start() // 启动线程
run() // 线程任务逻辑
sleep() // 线程休眠(毫秒),不释放锁
join() // 线程插队,等待该线程执行完毕
yield() // 线程礼让,让出CPU
setPriority() // 设置线程优先级
setDaemon() // 设置守护线程
currentThread()// 获取当前线程对象
3.1 线程休眠sleep
让线程暂停指定时间,时间到自动恢复执行。
java
Thread.sleep(1000); // 休眠1秒
3.2 守护线程(后台线程)
用户线程:前台任务,执行完程序才退出
守护线程:后台任务,所有用户线程结束,守护线程自动结束
典型场景:垃圾回收线程、日志监听、心跳检测。
java
t.setDaemon(true); // 设置为守护线程
四、线程生命周期(五大状态,面试必考)
线程从创建到销毁,一共5种状态,全程闭环流转。
4.1 五大状态详解
-
新建状态 NEW:new线程对象,未启动
-
就绪状态 RUNNABLE:调用start(),等待CPU调度
-
运行状态 RUNNING:抢到CPU时间片,执行run方法
-
阻塞状态 BLOCKED/WAITING:休眠、等待锁、礼让,暂停执行
-
死亡状态 TERMINATED:任务执行完毕或异常终止
核心流转:新建 → 就绪 → 运行 → (阻塞) → 死亡
五、多线程安全问题(核心痛点)
5.1 为什么会出现线程不安全?
三个必备条件(同时满足就不安全):
-
多线程并发执行
-
共享同一个资源(变量、数据)
-
对资源进行读写修改操作
5.2 线程不安全代码演示
模拟售票场景,多线程卖同100张票,出现超卖、重复卖票问题。
java
public class Ticket implements Runnable{
// 共享票数
private int ticket = 100;
@Override
public void run() {
while (ticket > 0){
System.out.println(Thread.currentThread().getName()+"卖出第"+ticket+"张票");
ticket--;
}
}
}
// 测试:三个线程同时卖票
public class Test {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(ticket,"窗口1").start();
new Thread(ticket,"窗口2").start();
new Thread(ticket,"窗口3").start();
}
}
运行结果:出现重复票、负数票,典型线程不安全!
5.3 最简解决方案:synchronized同步代码块
加锁!保证同一时间只有一个线程操作共享数据
java
@Override
public void run() {
while (true){
// 同步锁:锁对象唯一
synchronized (this) {
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+"卖出第"+ticket+"张票");
ticket--;
}else{
break;
}
}
}
}
加锁之后:线程串行执行,数据安全,不会超卖重复!
六、新手高频易错坑
-
start()才是启动线程,run()只是普通方法调用
-
线程启动后不一定立刻运行,需要抢夺CPU时间片
-
多线程操作共享变量必不安全,必须加锁
-
sleep休眠不释放锁,wait会释放锁
-
守护线程随用户线程消亡,适合后台任务
-
线程只能启动一次,重复start()直接报错
七、本篇总结
本篇搞定多线程全部入门核心,记住核心口诀:
-
进程是资源单位,线程是执行单位
-
线程三种创建:Thread、Runnable、Callable
-
Runnable开发首选,Callable适合需要返回值场景
-
线程五大生命周期:新建、就绪、运行、阻塞、死亡
-
多线程并发读写共享资源 = 线程不安全
-
同步锁synchronized解决线程安全问题