文章目录
-
- 一、核心概念:程序、进程、线程
-
- [1.1 基本定义](#1.1 基本定义)
- [1.2 核心关系](#1.2 核心关系)
- [二、Java 实现线程的三种方式](#二、Java 实现线程的三种方式)
-
- [2.1 方式 1:继承 `Thread` 类](#2.1 方式 1:继承
Thread类) -
- [2.1.1 实现步骤](#2.1.1 实现步骤)
- [2.1.2 完整代码示例](#2.1.2 完整代码示例)
- [2.1.3 核心注意点](#2.1.3 核心注意点)
- [2.2 方式 2:实现 `Runnable` 接口(推荐)](#2.2 方式 2:实现
Runnable接口(推荐)) -
- [2.2.1 实现步骤](#2.2.1 实现步骤)
- [2.2.2 完整代码示例](#2.2.2 完整代码示例)
- [2.2.3 核心优势](#2.2.3 核心优势)
- [2.3 方式 3:实现 `Callable` 接口](#2.3 方式 3:实现
Callable接口) -
- [2.3.1 适用场景](#2.3.1 适用场景)
- [2.3.2 实现步骤](#2.3.2 实现步骤)
- [2.3.3 完整代码示例](#2.3.3 完整代码示例)
- [2.1 方式 1:继承 `Thread` 类](#2.1 方式 1:继承
- [三、Thread vs Runnabl](#三、Thread vs Runnabl)
- 四、线程的生命周期
-
- [4.1 生命周期状态](#4.1 生命周期状态)
- [4.2 核心规则](#4.2 核心规则)
- 五、总结
一、核心概念:程序、进程、线程
1.1 基本定义
| 概念 | 本质 | 资源占用 | 作用 |
|---|---|---|---|
| 程序 | 存储在磁盘上的静态文件(代码文件 + 数据文件) | 无(仅占用磁盘空间) | 待执行的指令集合,无法解决问题 |
| 进程 | 正在运行的程序,操作系统进行资源分配的最小单元 | 独立内存空间、CPU、IO 等 | 调度系统资源解决具体问题 |
| 线程 | 进程的组成部分,CPU 调度执行任务的最小单元 | 共享所属进程的所有资源 | 实现进程内的并发任务 |
1.2 核心关系
- 一个进程至少包含一个线程(主线程,如
main方法对应的线程),也可包含多个线程 - 进程之间内存隔离,线程之间内存共享(多线程的核心优势)
- 操作系统负责管理进程和线程的调度(CPU 时间片轮转)
二、Java 实现线程的三种方式
2.1 方式 1:继承 Thread 类
2.1.1 实现步骤
- 定义类继承
java.lang.Thread - 重写
run()方法(线程的核心执行逻辑,无返回值) - 创建自定义线程类对象,调用
start()方法启动线程(不可直接调用run())
2.1.2 完整代码示例
java
public class MyThread extends Thread {
// 线程名称
String threadName;
// 构造方法初始化线程名称
public MyThread(String threadName) {
this.threadName = threadName;
}
// 重写run方法
@Override
public void run() {
// 模拟线程执行的循环任务
for (int i = 0; i < 100; i++) {
// 输出线程名称和循环次数
System.out.println(threadName + "--" + i);
// 休眠10ms,放大线程切换效果
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
}
// 测试方法
public static void main(String[] args) {
// 创建线程实例
MyThread mt1 = new MyThread("线程A");
MyThread mt2 = new MyThread("线程B");
// 启动线程
mt1.start();
mt2.start();
// 错误示例:重复启动同一线程,会报错
// mt1.start();
// 错误示例:直接调用run(),仅为普通方法调用,不会创建新线程
// mt1.run();
}
}
2.1.3 核心注意点
start():触发线程创建,等待 CPU 调度,由 JVM 调用run()run():普通方法,直接调用会在主线程中同步执行- 单继承限制:Java 不支持多继承,继承
Thread后无法继承其他类
2.2 方式 2:实现 Runnable 接口(推荐)
2.2.1 实现步骤
- 定义类实现
java.lang.Runnable接口 - 重写
run()方法(与 Thread 类的run()规则一致) - 创建自定义
Runnable实例,作为参数传入Thread构造方法 - 调用
Thread对象的start()方法启动线程
2.2.2 完整代码示例
java
public class MyRun implements Runnable {
// 共享变量(多个线程共用一个MyRun实例时,该变量会被共享)
int count = 0;
String taskName;
public MyRun(String taskName) {
this.taskName = taskName;
}
// 重写run方法
@Override
public void run() {
// 模拟任务执行
for (int i = 0; i < 100; i++) {
// 多线程共享count
count++;
System.out.println(Thread.currentThread().getName() + " | " + taskName + "--" + i + " | 累计计数:" + count);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
System.out.println(taskName + " 执行完成,最终计数:" + count);
}
// 测试方法
public static void main(String[] args) {
// 场景1:多个线程执行同一个任务(共享数据)
MyRun task = new MyRun("共享任务");
Thread t1 = new Thread(task, "线程C");
Thread t2 = new Thread(task, "线程D");
t1.start();
t2.start();
// 场景2:多个线程执行不同任务(独立数据)
MyRun task1 = new MyRun("独立任务1");
MyRun task2 = new MyRun("独立任务2");
Thread t3 = new Thread(task1, "线程E");
Thread t4 = new Thread(task2, "线程F");
t3.start();
t4.start();
}
}
2.2.3 核心优势
- 无单继承限制:实现接口后仍可继承其他类
- 任务与线程解耦:
Runnable封装任务逻辑,Thread负责执行,符合 "单一职责原则" - 支持数据共享:多个
Thread可共用一个Runnable实例,实现线程间数据共享
2.3 方式 3:实现 Callable 接口
2.3.1 适用场景
有返回值,可以完成需要获取线程执行结果任务。
2.3.2 实现步骤
- 定义类实现
java.util.concurrent.Callable<V>接口(V为返回值类型) - 重写
call()方法(有返回值) - 创建
Callable实例,封装到FutureTask<V>中 - 将
FutureTask传入Thread构造方法,调用start()启动线程 - 通过
FutureTask.get()获取线程执行结果
2.3.3 完整代码示例
java
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class MyCall implements Callable<Integer> {
int start;
int end;
public MyCall(int start, int end) {
this.start = start;
this.end = end;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = start; i <= end; i++) {
sum += i;
Thread.sleep(5);
}
return sum;
}
// 测试方法
public static void main(String[] args) {
// 创建Callable任务:计算1-100的和
Callable<Integer> callable = new MyCall(1, 100);
// 封装为FutureTask(实现了Runnable接口)
FutureTask<Integer> futureTask = new FutureTask<>(callable);
// 启动线程
Thread t = new Thread(futureTask);
t.start();
// 获取执行结果
try {
Integer result = futureTask.get();
System.out.println("1-100的和:" + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
三、Thread vs Runnabl
| 继承 Thread 类 | 实现 Runnable 接口 | |
|---|---|---|
| 继承限制 | 受限于 Java 单继承 | 无继承限制,可继承其他类 |
| 耦合性 | 线程与任务耦合(类既是线程也是任务) | 线程与任务解耦(Runnable 是任务,Thread 是执行器) |
| 数据共享 | 需通过静态变量实现,代码复杂 | 多个 Thread 共用一个 Runnable 实例,天然支持共享 |
| 代码扩展性 | 差(单继承限制) | 好(可实现多接口) |
| 适用场景 | 简单独立任务,无需继承其他类 | 复杂任务、需共享数据、需继承其他类 |
总结:
- 继承
Thread类受单继承限制,实现Runnable接口更灵活 Runnable接口实现了任务与线程的解耦Runnable支持多个线程共享同一个任务实例,便于线程间数据共享- 一般情况下更推荐使用
Runnable接口
四、线程的生命周期
4.1 生命周期状态
| 状态 | 说明 |
|---|---|
| 新建(New) | 创建 Thread 对象后,未调用 start() 前的状态 |
| 就绪(Runnable) | 调用 start() 后,线程进入就绪队列,等待 CPU 调度 |
| 运行(Running) | CPU 分配时间片,线程执行 run() 方法中的逻辑 |
| 阻塞(Blocked) | 线程因等待锁、IO、休眠等原因暂停执行,释放 CPU 资源 |
| 终止(Terminated) | 线程执行完 run() 方法,或因异常终止,生命周期结束 |
4.2 核心规则
- 线程从 "新建" 到 "终止" 只能一次,同一个 Thread 对象不能重复调用
start() - 线程的状态切换由 JVM 和操作系统共同控制,无法手动干预(如无法强制让线程从 "阻塞" 转为 "运行")
五、总结
- 核心概念:进程是资源分配单位,线程是 CPU 调度单位,多线程共享进程内存
- 实现方式:继承 Thread(简单但受限)、实现 Runnable(推荐,灵活共享)、实现 Callable(有返回值)
- 关键规则 :启动线程用
start()而非run(),线程生命周期仅一次,Runnable 天然支持数据共享 - 学习方向 :线程安全(同步锁)、线程池、并发工具类(
CountDownLatch、CyclicBarrier)等