作为Java新手,刚接触多线程时难免会被"进程""线程""生命周期"这些概念绕晕。本文结合相关核心知识点,用通俗的语言与可直接运行的代码,带你从零掌握Java线程的基础概念和核心实现方式,避开新手常见坑。
一、进程与线程到底是什么
在学线程前,必须先分清进程和线程的关系------它们是操作系统中最基础的执行单元,却有着本质区别。
1. 程序、进程、线程的区别
-
程序:就是我们写的代码文件(.java)和相关数据文件,存放在磁盘上,是"静态"的,本身不能解决任何问题。比如你写好的HelloWorld.java,只是一堆文字指令,不运行就毫无作用。
-
进程:是"正在运行中的程序",程序加载到内存后就变成了进程,是"动态"的。进程会占用CPU、内存(RAM)、IO(磁盘/网络)等系统资源,通过执行代码指令来解决实际问题。比如你双击运行Java程序,就启动了一个进程。
-
线程:是进程的"组成部分",一个进程至少有一个线程(称为主线程),也可以有多个线程。线程是CPU分配执行任务的最小单元,多个线程共享同一个进程的资源(数据、内存空间等),无需额外分配资源,切换效率比进程高得多。
2. 为什么需要多线程
当一个程序需要同时执行多个任务时,多线程就能派上用场。比如我们看视频时,视频播放、声音输出、弹幕滚动就是三个独立任务,对应三个线程同时工作,才能保证体验流畅。如果用单线程,只能逐个执行任务,会出现"卡顿"。
3.核心结论
操作系统分配资源的最小单元是进程 ,CPU执行任务的最小单元是线程 ;多线程的核心优势是共享进程资源、提高任务执行效率。
二、Java实现线程的三种方式
Java提供了三种创建线程的方式,其中前两种是日常开发和面试的重点,新手务必熟练掌握。所有代码均补全语法细节,可直接复制运行。
方式一:继承Thread类
Thread类是Java自带的线程基类,继承它后重写run()方法,就能定义线程执行逻辑。步骤如下:
1. 实现步骤
- 定义一个类,继承Thread类;
- 重写Thread类的run()方法(线程的核心执行逻辑写在这里);
- 创建自定义线程类的对象;
- 调用对象的start()方法启动线程(注意:不是直接调用run()方法)。
2. 完整代码(带详细注释)
java
// 1. 继承Thread类
public class MyThread extends Thread {
// 定义线程名称,方便区分多个线程
private String name;
// 构造方法,初始化线程名称
public MyThread(String name) {
this.name = name;
}
// 2. 重写run()方法,定义线程执行逻辑
@Override
public void run() {
// 这里模拟线程执行的任务:循环打印线程名称和计数
for (int i = 0; i < 100; i++) {
System.out.println(name + "--" + i);
}
}
// 主线程(程序入口)
public static void main(String[] args) {
// 3. 创建两个线程对象
MyThread mt1 = new MyThread("线程A");
MyThread mt2 = new MyThread("线程B");
// 4. 调用start()方法启动线程
mt1.start();
mt2.start();
// 错误示范:不能直接调用run(),否则不会启动新线程,只是普通方法调用
// mt1.run();
}
}
3. 运行效果与说明
-
定义一个类,实现Runnable接口;
-
重写Runnable接口的run()方法(定义线程执行逻辑);
-
创建自定义Runnable类的对象;
-
将Runnable对象作为参数,传入Thread类的构造方法,创建Thread对象;
-
调用Thread对象的start()方法启动线程。
方式二:实现Runnable接口
Java是单继承语言,若自定义类已继承其他类,就无法再继承Thread类。此时实现Runnable接口是更灵活的选择,也是开发中更推荐的方式。
1. 实现步骤
-
定义一个类,实现Runnable接口;
-
重写Runnable接口的run()方法(定义线程执行逻辑);
-
创建自定义Runnable类的对象;
-
将Runnable对象作为参数,传入Thread类的构造方法,创建Thread对象;
-
调用Thread对象的start()方法启动线程。
2. 完整代码(带详细注释)
java
// 1. 实现Runnable接口
public class MyRun implements Runnable {
private String name;
// 定义共享变量,演示多线程共享资源
private int count = 0;
// 构造方法,初始化线程名称
public MyRun(String name) {
this.name = name;
}
// 2. 重写run()方法,定义线程执行逻辑
@Override
public void run() {
for (int i = 0; i < 100; i++) {
count++; // 多个线程共享count变量
System.out.println(name + "--" + i + ",当前count:" + count);
}
System.out.println(name + "执行完毕,最终count:" + count);
}
public static void main(String[] args) {
// 3. 创建Runnable对象
MyRun mr = new MyRun("共享线程");
// 4. 将Runnable对象传入Thread构造方法,创建两个Thread对象
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
// 5. 启动线程
t1.start();
t2.start();
}
}
方式三:实现Callable接口(了解即可)
前两种方式的run()方法无返回值、无法抛出受检异常。Callable接口弥补了这一缺陷,支持线程执行后返回结果,但实现相对复杂,新手先掌握前两种,后续进阶再深入。
三、新手必记:线程的核心注意事项
-
同一个Thread对象不能重复调用start()方法:线程启动后会进入生命周期,一个线程只能启动一次,重复调用会抛出IllegalThreadStateException异常。
-
线程的生命周期:核心分为"创建(new对象)→ 就绪(start()后)→ 运行(CPU调度)→ 销毁",每个线程在生命周期内只能启动一次。
-
多线程共享资源:通过实现Runnable接口的方式,多个线程可共享同一个Runnable对象的属性,这是继承Thread类难以实现的(继承方式每个线程对象独立)。
四、面试高频题:继承Thread vs 实现Runnable
这是新手面试必问的基础题,从三个维度清晰区分:
| 对比维度 | 继承Thread类 | 实现Runnable接口 |
|---|---|---|
| 继承限制 | Java单继承,子类无法再继承其他类 | 无继承限制,可同时实现多个接口 |
| 资源共享 | 每个线程对象独立,难以共享资源(需用static变量,不推荐) | 多个线程可共用一个Runnable对象,轻松共享资源 |
| 代码耦合度 | 线程逻辑与Thread类耦合,灵活性低 | 线程逻辑与Thread类分离,符合"面向接口编程",灵活性高 |
结论:开发中优先选择实现Runnable接口,兼顾灵活性和资源共享需求。
五、前置知识补充(新手必备)
要学好线程,需先掌握以下Java基础,否则会理解困难:
-
Java基础语法:数据类型、变量、运算符、条件判断(if/else)、循环语句(for/while)、数组。
-
面向对象基础:类与对象、继承、封装、接口、泛型(理解接口的实现逻辑)。
-
计算机基础知识:操作系统(进程/线程调度)、计算机组成原理(CPU、内存、IO的作用)、JVM(Java虚拟机对线程的管理)。
六、新手实操建议
-
复制本文代码,逐行敲写并运行,观察多线程交替执行的效果;
-
修改循环次数(如改成1000次),观察线程切换的随机性。
七、总结
Java线程的核心是"多任务并发执行",新手先掌握"继承Thread类"和"实现Runnable接口"两种方式,理解进程与线程的区别、线程生命周期和资源共享特点,再通过实操巩固。后续我们会结合智能仿真无人机项目,讲解线程在实战中的应用,以及线程安全、线程同步等进阶知识点,敬请期待!