线程的认识
线程是进程的最小执行单元,共享进程资源,切换开销小;线程创建优先选择Runnable(无单继承限制)或Callable(带返回值);线程状态转换是核心,需理解RUNNABLE与BLOCKEDWAITING的区别;线程安全的核心是 “共享资源的原子操作”,通过Lock、原子类等实现;避免使用废弃方法(如stop()),通过标志位、等实现优雅线程控制。
在 Java 中,线程(Thread) 是程序执行的最小单元,是进程的一部分。一个进程可以包含多个线程,这些线程共享进程的内存空间(如方法区、堆),但拥有各自独立的程序计数器、虚拟机栈和本地方法栈,因此线程切换的开销远小于进程。
一、线程的核心概念
1. 线程与进程的区别
| 维度 | 进程 | 线程 |
|---|---|---|
| 资源占用 | 独立占有内存、文件句柄等 | 共享进程资源,仅私有少量数据(程序计数器、栈) |
| 切换开销 | 大(需切换进程上下文) | 小(仅切换线程上下文) |
| 通信方式 | 复杂(如管道、Socket) | 简单(直接操作共享变量) |
| 独立性 | 高(一个进程崩溃不影响其他) | 低(一个线程崩溃可能导致整个进程崩溃) |
2. 线程的状态(生命周期)
Java 线程的状态定义在 Thread.State 枚举中,共 6 种,状态转换是线程的核心知识点:
| 状态 | 说明 |
|---|---|
NEW(新建) |
线程对象已创建,但未调用 start() 方法(未启动) |
RUNNABLE(可运行) |
调用 start() 后,线程处于 “就绪” 或 “运行中” 状态(取决于 CPU 调度) |
BLOCKED(阻塞) |
线程因竞争同步锁(synchronized)被阻塞,等待锁释放 |
WAITING(等待) |
线程通过 wait()、join() 等方法主动放弃 CPU,需其他线程唤醒 |
TIMED_WAITING(超时等待) |
线程通过 sleep(long)、wait(long) 等方法等待指定时间,超时自动唤醒 |
TERMINATED(终止) |
线程执行完毕(run() 方法结束)或异常终止 |
状态转换图核心逻辑:NEW → RUNNABLE(调用 start())→ 执行 run() → TERMINATED;RUNNABLE 可因锁竞争进入 BLOCKED,获锁后回到 RUNNABLE;RUNNABLE 可因 sleep()/wait()/join() 进入 WAITING/TIMED_WAITING,唤醒后回到 RUNNABLE。
二、Java 中创建线程的 3 种方式
1. 继承 Thread 类(重写 run() 方法)
Thread 类本身实现了 Runnable 接口,核心是重写 run() 方法(线程执行体),通过 start() 方法启动线程(而非直接调用 run())。
2. 实现 Runnable 接口(推荐)
Runnable 是函数式接口(仅含 void run() 方法),避免单继承限制,更符合 “职责分离”(线程执行逻辑与线程对象分离)。
java
运行
// 1. 实现 Runnable 接口
public class RunnableDemo implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// 2. 创建 Runnable 实例,作为 Thread 的构造参数
Runnable task = new RunnableDemo();
Thread t1 = new Thread(task, "线程A");
Thread t2 = new Thread(task, "线程B");
t1.start();
t2.start();
}
}
// 简化:使用 Lambda 表达式(Java 8+)
public class LambdaRunnable {
public static void main(String[] args) {
Thread t = new Thread(() -> {
System.out.println("Lambda 线程执行");
}, "Lambda线程");
t.start();
}
}
3. 实现 Callable 接口(带返回值 + 可抛异常)
Runnable 无返回值、无法抛出受检异常,Callable<V> 接口弥补了这一缺陷(含 V call() throws Exception 方法),需配合 FutureTask 使用(FutureTask 实现了 RunnableFuture 接口,兼具 Runnable 和 Future 特性)。
java
运行
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class CallableDemo implements Callable<Integer> {
// 重写 call() 方法,返回结果并可抛异常
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 10; i++) {
sum += i;
Thread.sleep(50);
}
return sum; // 返回计算结果
}
public static void main(String[] args) throws Exception {
// 1. 创建 Callable 实例
Callable<Integer> task = new CallableDemo();
// 2. 包装为 FutureTask(接收返回值)
FutureTask<Integer> futureTask = new FutureTask<>(task);
// 3. 启动线程
new Thread(futureTask, "计算线程").start();
// 4. 获取返回值(get() 方法会阻塞,直到线程执行完毕)
Integer result = futureTask.get();
System.out.println("1-10 求和结果:" + result); // 输出 55
}
}
核心优势:可获取线程执行结果,适合需要异步计算并返回结果的场景(如多线程下载文件后合并结果)。
三、线程的核心方法
1. 线程启动与终止
start():启动线程,JVM 调用run()方法(不能重复调用);run():线程执行体,存放业务逻辑(直接调用仅为普通方法,不会启动新线程);stop()(已废弃):强制终止线程,可能导致资源泄露(如锁未释放),推荐通过 “标志位” 优雅终止:
java
运行
public class StopThreadDemo implements Runnable {
private volatile boolean isRunning = true; // volatile 保证可见性
@Override
public void run() {
while (isRunning) {
System.out.println("线程运行中...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程优雅终止");
}
public void stop() {
isRunning = false; // 修改标志位
}
public static void main(String[] args) throws InterruptedException {
StopThreadDemo task = new StopThreadDemo();
Thread t = new Thread(task);
t.start();
Thread.sleep(500); // 主线程休眠 500ms
task.stop(); // 终止线程
}
}
2. 线程休眠与等待
sleep(long millis):线程休眠指定时间(毫秒),不释放锁,休眠期间 CPU 可调度其他线程;wait()/wait(long):线程主动放弃 CPU,释放锁,进入等待队列,需通过notify()/notifyAll()唤醒(必须在synchronized代码块中调用);join()/join(long):让当前线程等待目标线程执行完毕后再继续(如主线程等待子线程执行完再退出):
java
运行
public class JoinDemo {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
for (int i = 0; i < 3; i++) {
System.out.println("子线程:" + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
t.join(); // 主线程等待 t 线程执行完毕
System.out.println("主线程继续执行"); // 子线程执行完后才输出
}
}
3. 线程优先级与守护线程
- 优先级:
Thread.MIN_PRIORITY(1)~Thread.MAX_PRIORITY(10),默认Thread.NORM_PRIORITY(5),仅为 CPU 调度的 “建议”,不保证优先级高的线程先执行; - 守护线程(Daemon Thread):为其他线程服务(如垃圾回收线程),当所有非守护线程结束,守护线程自动退出,通过
setDaemon(true)设置(必须在start()前调用):
java
运行
public class DaemonDemo {
public static void main(String[] args) {
Thread daemonThread = new Thread(() -> {
while (true) {
System.out.println("守护线程运行中...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
daemonThread.setDaemon(true); // 设置为守护线程
daemonThread.start();
// 主线程(非守护线程)执行 300ms 后退出
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程退出,守护线程自动终止");
}
}
四、线程安全问题
1. 问题根源
多个线程并发访问共享资源(如共享变量、集合),且存在修改操作,导致数据不一致(如超卖、计数错误)。
java
运行
public class ThreadDemo extends Thread {
@Override
public void run() {
// 线程执行逻辑
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
try {
Thread.sleep(100); // 线程休眠 100ms,释放 CPU(不会释放锁)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread t1 = new ThreadDemo();
Thread t2 = new ThreadDemo();
t1.setName("线程1");
t2.setName("线程2");
t1.start(); // 启动线程1(底层调用 native 方法 start0(),由 JVM 调用 run())
t2.start(); // 启动线程2
}
}
注意:
- 不能多次调用
start(),否则抛出IllegalThreadStateException; - 继承
Thread后无法再继承其他类(Java 单继承限制)。
2. 解决方式
- 同步代码块:
synchronized (锁对象) { 共享资源操作 }; - 同步方法:在方法上添加
synchronized关键字(锁对象为this或类对象); - Lock 锁(JUC 并发包):
ReentrantLock等,比synchronized更灵活(可中断、超时获取锁); - 原子类:
AtomicInteger、AtomicLong等,基于 CAS 机制实现无锁线程安全。
示例(synchronized 解决计数安全):
java
运行
public class ThreadSafeDemo {
private static int count = 0;
private static final Object LOCK = new Object(); // 锁对象
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
synchronized (LOCK) { // 同步代码块,保证原子操作
count++;
}
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("最终计数:" + count); // 输出 2000(线程安全)
}
}
五、核心总结
- 线程是进程的最小执行单元,共享进程资源,切换开销小;
- 线程创建优先选择
Runnable(无单继承限制)或Callable(带返回值); - 线程状态转换是核心,需理解
RUNNABLE与BLOCKED/WAITING的区别; - 线程安全的核心是 “共享资源的原子操作”,通过
synchronized、Lock、原子类等实现; - 避免使用废弃方法(如
stop()),通过标志位、interrupt()等实现优雅线程控制。
更多推荐


所有评论(0)