在 Java 并发编程中,Thread 类是最基础的线程操作单元。理解 Thread 类的正确使用方式,是掌握 Java 多线程编程的第一步。我们先来看 Thread 类的几种常见构造方法:
Thread 类提供了多个构造方法,每个都有其特定的使用场景:
java复制// 最基本的构造方法,创建一个新的线程对象
Thread t1 = new Thread();
// 传入 Runnable 对象的构造方法
Thread t2 = new Thread(new MyRunnable());
// 指定线程名称的构造方法
Thread t3 = new Thread("这是我的名字");
// 同时指定 Runnable 对象和线程名称
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");
在实际开发中,最常用的是传入 Runnable 对象的构造方式。这种方式将线程的执行逻辑与线程对象本身解耦,更符合面向对象的设计原则。
注意:直接继承 Thread 类并重写 run() 方法的方式虽然可行,但不推荐。这种方式会限制类的继承结构,降低代码的灵活性。
每个 Thread 对象都包含一些重要的属性信息,了解这些属性对于调试和优化多线程程序很有帮助:
| 属性 | 获取方法 | 说明 |
|---|---|---|
| ID | getId() | 线程的唯一标识符,JVM 分配,不可修改 |
| 名称 | getName() | 线程名称,可用于调试,建议为重要线程设置描述性名称 |
| 状态 | getState() | 反映线程当前状态(NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED) |
| 优先级 | getPriority() | 线程调度优先级(1-10),但实际效果依赖操作系统实现 |
| 是否守护线程 | isDaemon() | 守护线程不会阻止 JVM 退出 |
| 是否存活 | isAlive() | 线程是否已启动且尚未终止 |
| 中断状态 | isInterrupted() | 线程是否被中断 |
守护线程(Daemon Thread)是 Java 线程的一个重要概念:
java复制Thread daemonThread = new Thread(() -> {
while (true) {
System.out.println("守护线程运行中...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
daemonThread.setDaemon(true); // 设置为守护线程
daemonThread.start();
关键特点:
警告:不要在守护线程中执行关键任务或 I/O 操作,因为这些操作可能在未完成时就被 JVM 终止。
启动线程的正确方式是调用 start() 方法,而不是直接调用 run() 方法:
java复制Thread thread = new Thread(() -> {
System.out.println("线程执行中...");
});
thread.start(); // 正确方式 - 创建新线程执行
// thread.run(); // 错误方式 - 在当前线程直接调用
start() 方法的关键特性:
当调用 start() 方法后,线程的执行流程如下:
Java 中的线程中断是一种协作机制,用于通知线程应该停止当前工作。中断不是强制性的,线程可以选择忽略中断请求。
| 方法 | 说明 |
|---|---|
| interrupt() | 设置线程的中断标志位,如果线程处于阻塞状态,会抛出 InterruptedException |
| isInterrupted() | 检查线程的中断状态,不会清除中断标志 |
| static interrupted() | 检查当前线程的中断状态,并清除中断标志 |
正确处理中断对于编写健壮的多线程程序至关重要:
java复制public class ProperInterruption implements Runnable {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
// 模拟工作
System.out.println("Working...");
Thread.sleep(1000);
} catch (InterruptedException e) {
// 恢复中断状态
Thread.currentThread().interrupt();
System.out.println("Thread was interrupted during sleep");
break;
}
}
System.out.println("Thread exiting...");
}
}
关键要点:
许多阻塞方法(如 sleep(), wait(), join())都会响应中断:
java复制Thread worker = new Thread(() -> {
try {
Thread.sleep(10000); // 可能被中断
} catch (InterruptedException e) {
System.out.println("Sleep interrupted");
// 恢复中断状态
Thread.currentThread().interrupt();
}
});
worker.start();
// 稍后中断线程
Thread.sleep(1000);
worker.interrupt();
join() 方法允许一个线程等待另一个线程完成:
java复制Thread worker = new Thread(() -> {
System.out.println("Worker started");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Worker completed");
});
System.out.println("Main thread starting worker");
worker.start();
System.out.println("Main thread waiting for worker");
worker.join(); // 主线程在此等待worker完成
System.out.println("Main thread continuing");
| 方法签名 | 说明 |
|---|---|
| void join() | 无限期等待,直到目标线程终止 |
| void join(long millis) | 最多等待指定毫秒数 |
| void join(long millis, int nanos) | 更高精度的等待(理论上,实际精度依赖系统) |
在实际应用中,应该避免无限期等待:
java复制Thread worker = new Thread(() -> {
// 模拟长时间任务
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
worker.start();
try {
// 最多等待2秒
worker.join(2000);
if (worker.isAlive()) {
System.out.println("Worker is taking too long, giving up");
worker.interrupt();
}
} catch (InterruptedException e) {
System.out.println("Main thread was interrupted while waiting");
}
Java 线程的生命周期包含以下状态:
java复制public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
java复制Thread thread = new Thread(() -> {
try {
Thread.sleep(1000);
synchronized (Thread.currentThread()) {
Thread.currentThread().wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println("Before start: " + thread.getState()); // NEW
thread.start();
System.out.println("After start: " + thread.getState()); // RUNNABLE
Thread.sleep(500);
System.out.println("During sleep: " + thread.getState()); // TIMED_WAITING
Thread.sleep(600);
System.out.println("After sleep: " + thread.getState()); // WAITING
thread.interrupt();
thread.join();
System.out.println("After termination: " + thread.getState()); // TERMINATED
Java 允许为线程设置优先级,但实际效果依赖操作系统:
java复制Thread highPriority = new Thread(() -> {
System.out.println("High priority thread running");
});
highPriority.setPriority(Thread.MAX_PRIORITY); // 10
Thread lowPriority = new Thread(() -> {
System.out.println("Low priority thread running");
});
lowPriority.setPriority(Thread.MIN_PRIORITY); // 1
注意:过度依赖线程优先级可能导致程序在不同平台表现不一致。优先级应该仅作为提示,而不是保证。
yield() 方法提示调度器当前线程愿意让出 CPU:
java复制Thread.yield(); // 提示调度器可以切换到其他线程
但要注意:
为线程设置有意义的名称有助于调试:
java复制Thread parserThread = new Thread(() -> {
// 解析任务
}, "XML-Parser-Thread");
Thread networkThread = new Thread(() -> {
// 网络操作
}, "Network-IO-Thread");
为线程设置未捕获异常处理器:
java复制Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
System.err.println("Uncaught exception in thread " + thread.getName());
throwable.printStackTrace();
});
Thread faultyThread = new Thread(() -> {
throw new RuntimeException("Something went wrong!");
});
faultyThread.start();
使用 ThreadLocal 为每个线程维护独立变量:
java复制ThreadLocal<Integer> threadId = new ThreadLocal<>() {
@Override
protected Integer initialValue() {
return (int) (Math.random() * 1000);
}
};
new Thread(() -> {
System.out.println("Thread ID: " + threadId.get());
}).start();
new Thread(() -> {
System.out.println("Thread ID: " + threadId.get());
}).start();
java复制// 不安全的计数器实现
class UnsafeCounter {
private int count = 0;
public void increment() {
count++;
}
public int getCount() {
return count;
}
}
// 测试代码
UnsafeCounter counter = new UnsafeCounter();
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 100; i++) {
Thread t = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
counter.increment();
}
});
threads.add(t);
t.start();
}
for (Thread t : threads) {
t.join();
}
System.out.println("Final count: " + counter.getCount()); // 可能小于100000
解决方案:使用 synchronized 或 AtomicInteger
java复制// 死锁示例
Object lock1 = new Object();
Object lock2 = new Object();
Thread t1 = new Thread(() -> {
synchronized (lock1) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("Thread 1 got both locks");
}
}
});
Thread t2 = new Thread(() -> {
synchronized (lock2) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("Thread 2 got both locks");
}
}
});
t1.start();
t2.start();
预防死锁的策略:
线程也是系统资源,不当管理会导致泄漏:
java复制// 错误示例:创建大量线程而不管理
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
解决方案:使用线程池管理线程资源
虽然 Thread 类是 Java 并发的基础,但在现代 Java 开发中,更推荐使用高级并发工具:
java复制// 使用 ExecutorService 的示例
ExecutorService executor = Executors.newFixedThreadPool(4);
Future<Integer> future = executor.submit(() -> {
// 执行耗时计算
return 42;
});
// 可以继续做其他工作...
try {
Integer result = future.get(); // 阻塞直到结果可用
System.out.println("Result: " + result);
} catch (ExecutionException e) {
e.printStackTrace();
} finally {
executor.shutdown();
}
在实际项目中,合理选择并发工具可以显著提高程序性能和可维护性。Thread 类作为基础,理解其原理对于掌握更高级的并发概念至关重要。