1. Java线程基础与Thread类核心用法
多线程编程是现代Java开发者的必备技能。记得第一次接触Thread类时,我被那个不断跳动的控制台输出震撼到了——原来程序可以这样"分身"执行任务。作为Java并发编程的基石,Thread类虽然API看似简单,但实际应用中藏着不少"坑"。今天我们就来彻底拆解这个陪伴Java开发者25年的老朋友。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 线程创建与启动
2.1 继承Thread类方式
这是最直观的创建方式,适合简单的线程任务:
java复制class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()
+ " 执行: " + i);
try {
Thread.sleep(300); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 启动线程
new MyThread().start();
关键点:必须重写run()方法而非直接调用,start()方法会触发JVM创建新线程
2.2 实现Runnable接口方式
更推荐的实践方式,避免了Java单继承的限制:
java复制class MyRunnable implements Runnable {
@Override
public void run() {
// 线程执行逻辑
}
}
// 使用方式
Thread thread = new Thread(new MyRunnable());
thread.start();
实际开发中,我90%的情况都会选择Runnable方式。它不仅更灵活,还能方便地与线程池配合使用。
3. 线程生命周期控制
3.1 状态转换详解
Java线程有6种明确状态:
- NEW - 刚创建未启动
- RUNNABLE - 可运行状态(包括就绪和运行中)
- BLOCKED - 等待监视器锁
- WAITING - 无限期等待
- TIMED_WAITING - 有限期等待
- TERMINATED - 线程结束
通过getState()方法可以获取当前状态,调试时非常有用:
java复制Thread thread = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
System.out.println(thread.getState()); // NEW
thread.start();
System.out.println(thread.getState()); // RUNNABLE
3.2 线程中断机制
正确的中断处理是健壮多线程程序的关键:
java复制Thread worker = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
// 执行任务
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// 恢复中断状态
Thread.currentThread().interrupt();
break;
}
}
});
worker.start();
// 需要停止时
worker.interrupt();
重要原则:不要简单吞掉InterruptedException,要么重新设置中断状态,要么妥善处理终止逻辑
4. 线程同步与通信
4.1 synchronized关键字
最基本的同步手段,可用于方法或代码块:
java复制class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public void safeAdd() {
synchronized(this) {
count += 2;
}
}
}
4.2 wait/notify机制
经典的线程间通信方式:
java复制class MessageQueue {
private String message;
private boolean empty = true;
public synchronized String take() {
while (empty) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
empty = true;
notifyAll();
return message;
}
public synchronized void put(String message) {
while (!empty) {
try {
wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
empty = false;
this.message = message;
notifyAll();
}
}
5. 线程池与高级特性
5.1 ThreadLocal原理
线程局部变量的典型应用:
java复制class UserContext {
private static final ThreadLocal<User> currentUser = new ThreadLocal<>();
public static void set(User user) {
currentUser.set(user);
}
public static User get() {
return currentUser.get();
}
public static void remove() {
currentUser.remove();
}
}
// 使用示例
UserContext.set(new User("admin"));
// 在同线程任何地方都可以获取
User user = UserContext.get();
内存泄漏警告:必须在使用完成后调用remove(),特别是在线程池环境中
5.2 守护线程
JVM退出时不等待的线程类型:
java复制Thread daemon = new Thread(() -> {
while (true) {
System.out.println("Daemon working...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
}
});
daemon.setDaemon(true); // 必须在start前设置
daemon.start();
6. 实战问题排查
6.1 常见异常处理
"Exception in thread main"类问题通常源于:
- 类路径配置错误
- 版本兼容性问题
- 方法签名不匹配
解决方案步骤:
- 检查完整异常堆栈
- 确认依赖版本一致性
- 使用javap验证方法签名
6.2 线程死锁检测
使用jstack工具分析:
bash复制jstack -l <pid> > thread_dump.txt
典型死锁特征:
- 多个线程互相持有对方需要的锁
- 线程状态为BLOCKED
- 存在循环等待链
7. 性能优化技巧
7.1 上下文切换优化
- 避免过度创建线程(理想线程数 ≈ CPU核心数 × (1 + 等待时间/计算时间))
- 使用线程池控制并发量
- 减少锁竞争(缩小同步范围、使用读写锁)
7.2 锁优化实践
- 偏向锁:-XX:+UseBiasedLocking
- 自旋锁:-XX:+UseSpinning
- 锁消除:-XX:+EliminateLocks
- 锁粗化:合并相邻同步块
8. Java线程演进
随着Java版本更新,线程API也在持续增强:
- Java 5: Executor框架
- Java 7: ForkJoinPool
- Java 8: CompletableFuture
- Java 9: 改进的Thread API
- Java 19: 虚拟线程预览
虽然现在有更高级的并发工具,但理解Thread类的工作原理仍然是每个Java开发者必须打好的基础。就像我 mentor 常说的:"不会用Thread的程序员,用不好ExecutorService"。
