文章目录
- 一、核心概念:程序、进程、线程
- 1.1 基本定义
- 1.2 核心关系
- 二、Java 实现线程的三种方式
- 2.1 方式 1:继承 `Thread` 类
- 2.1.1 实现步骤
- 2.1.2 完整代码示例
- 2.1.3 核心注意点
- 2.2 方式 2:实现 `Runnable` 接口(推荐)
- 2.2.1 实现步骤
- 2.2.2 完整代码示例
- 2.2.3 核心优势
- 2.3 方式 3:实现 `Callable` 接口
- 2.3.1 适用场景
- 2.3.2 实现步骤
- 2.3.3 完整代码示例
- 三、Thread vs Runnabl
- 四、线程的生命周期
- 4.1 生命周期状态
- 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 完整代码示例
publicclassMyThreadextendsThread{// 线程名称StringthreadName;// 构造方法初始化线程名称publicMyThread(StringthreadName){this.threadName=threadName;}// 重写run方法@Overridepublicvoidrun(){// 模拟线程执行的循环任务for(inti=0;i<100;i++){// 输出线程名称和循环次数System.out.println(threadName+"--"+i);// 休眠10ms,放大线程切换效果try{Thread.sleep(10);}catch(InterruptedExceptione){Thread.currentThread().interrupt();e.printStackTrace();}}}// 测试方法publicstaticvoidmain(String[]args){// 创建线程实例MyThreadmt1=newMyThread("线程A");MyThreadmt2=newMyThread("线程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 完整代码示例
publicclassMyRunimplementsRunnable{// 共享变量(多个线程共用一个MyRun实例时,该变量会被共享)intcount=0;StringtaskName;publicMyRun(StringtaskName){this.taskName=taskName;}// 重写run方法@Overridepublicvoidrun(){// 模拟任务执行for(inti=0;i<100;i++){// 多线程共享countcount++;System.out.println(Thread.currentThread().getName()+" | "+taskName+"--"+i+" | 累计计数:"+count);try{Thread.sleep(10);}catch(InterruptedExceptione){Thread.currentThread().interrupt();e.printStackTrace();}}System.out.println(taskName+" 执行完成,最终计数:"+count);}// 测试方法publicstaticvoidmain(String[]args){// 场景1:多个线程执行同一个任务(共享数据)MyRuntask=newMyRun("共享任务");Threadt1=newThread(task,"线程C");Threadt2=newThread(task,"线程D");t1.start();t2.start();// 场景2:多个线程执行不同任务(独立数据)MyRuntask1=newMyRun("独立任务1");MyRuntask2=newMyRun("独立任务2");Threadt3=newThread(task1,"线程E");Threadt4=newThread(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 完整代码示例
importjava.util.concurrent.Callable;importjava.util.concurrent.ExecutionException;importjava.util.concurrent.FutureTask;publicclassMyCallimplementsCallable<Integer>{intstart;intend;publicMyCall(intstart,intend){this.start=start;this.end=end;}@OverridepublicIntegercall()throwsException{intsum=0;for(inti=start;i<=end;i++){sum+=i;Thread.sleep(5);}returnsum;}// 测试方法publicstaticvoidmain(String[]args){// 创建Callable任务:计算1-100的和Callable<Integer>callable=newMyCall(1,100);// 封装为FutureTask(实现了Runnable接口)FutureTask<Integer>futureTask=newFutureTask<>(callable);// 启动线程Threadt=newThread(futureTask);t.start();// 获取执行结果try{Integerresult=futureTask.get();System.out.println("1-100的和:"+result);}catch(InterruptedException|ExecutionExceptione){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)等