第2章
🧵 线程基础操作
掌握Java中创建线程的多种方式、线程属性设置以及异常处理机制
学习目标
- 掌握Java中创建线程的多种方式
- 理解Thread类和Runnable接口的区别
- 学会线程的启动、暂停和停止
- 掌握线程的基本属性设置
- 了解线程的异常处理机制
继承Thread类
继承Thread类是创建线程最直接的方式。通过重写run()方法来定义线程要执行的任务,然后调用start()方法启动线程。
核心概念
run()方法定义线程要执行的任务,start()方法启动线程。直接调用run()方法不会创建新线程,而是在当前线程中执行。
基本用法
ThreadExample.java
public class ThreadExample extends Thread {
private String threadName;
public ThreadExample(String name) {
this.threadName = name;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(threadName + " 执行第 " + (i + 1) + " 次");
try {
Thread.sleep(1000); // 暂停1秒
} catch (InterruptedException e) {
System.out.println(threadName + " 被中断");
return;
}
}
System.out.println(threadName + " 执行完成");
}
public static void main(String[] args) {
// 创建线程对象
ThreadExample thread1 = new ThreadExample("线程1");
ThreadExample thread2 = new ThreadExample("线程2");
// 启动线程
thread1.start();
thread2.start();
System.out.println("主线程执行完成");
}
}
run()和start()的区别
start()方法
- 创建新的线程
- 在新线程中执行run()方法
- 只能调用一次
- 异步执行
run()方法
- 不创建新线程
- 在当前线程中执行
- 可以多次调用
- 同步执行
实现Runnable接口
实现Runnable接口是创建线程的推荐方式。这种方式避免了Java单继承的限制,使得类可以继承其他类的同时实现多线程功能。
基本实现
RunnableExample.java
public class RunnableExample implements Runnable {
private String taskName;
public RunnableExample(String name) {
this.taskName = name;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(taskName + " 执行第 " + (i + 1) + " 次");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(taskName + " 被中断");
return;
}
}
System.out.println(taskName + " 执行完成");
}
public static void main(String[] args) {
// 创建Runnable对象
RunnableExample task1 = new RunnableExample("任务1");
RunnableExample task2 = new RunnableExample("任务2");
// 创建Thread对象并启动
Thread thread1 = new Thread(task1);
Thread thread2 = new Thread(task2);
thread1.start();
thread2.start();
System.out.println("主线程执行完成");
}
}
Lambda表达式创建线程
LambdaThreadExample.java
public class LambdaThreadExample {
public static void main(String[] args) {
// 使用Lambda表达式创建线程
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Lambda线程执行第 " + (i + 1) + " 次");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Lambda线程被中断");
return;
}
}
});
// 使用方法引用
Thread thread2 = new Thread(LambdaThreadExample::printNumbers);
thread1.start();
thread2.start();
}
private static void printNumbers() {
for (int i = 1; i <= 5; i++) {
System.out.println("方法引用线程: " + i);
try {
Thread.sleep(800);
} catch (InterruptedException e) {
System.out.println("方法引用线程被中断");
return;
}
}
}
}
Runnable接口的优势
- 避免单继承限制:类可以继承其他类的同时实现Runnable
- 代码复用:同一个Runnable对象可以被多个线程使用
- 职责分离:任务逻辑与线程管理分离
- 更好的设计:符合面向对象设计原则
使用Callable接口
Callable接口与Runnable类似,但它可以返回结果并抛出异常。通常与Future接口配合使用,可以获取线程执行的结果。
基本用法
CallableExample.java
import java.util.concurrent.*;
public class CallableExample {
public static void main(String[] args) {
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(2);
// 创建Callable任务
Callable task1 = () -> {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
Thread.sleep(10); // 模拟计算时间
}
return sum;
};
Callable task2 = () -> {
Thread.sleep(2000);
return "Hello from Callable!";
};
try {
// 提交任务并获取Future对象
Future future1 = executor.submit(task1);
Future future2 = executor.submit(task2);
// 获取结果(会阻塞直到任务完成)
System.out.println("计算结果: " + future1.get());
System.out.println("字符串结果: " + future2.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
executor.shutdown();
}
}
}
Future接口的方法
get()方法
获取任务执行结果,会阻塞直到任务完成。可以设置超时时间。
cancel()方法
取消任务执行。如果任务已经开始执行,可以选择是否中断。
isDone()方法
检查任务是否已经完成(正常完成、异常或取消)。
线程属性设置
线程具有多种属性,如名称、优先级、守护线程状态等。合理设置这些属性可以帮助我们更好地管理和调试线程。
线程属性示例
ThreadPropertiesExample.java
public class ThreadPropertiesExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() +
" 优先级: " + Thread.currentThread().getPriority() +
" 是否守护线程: " + Thread.currentThread().isDaemon());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("线程被中断");
return;
}
}
});
// 设置线程名称
thread.setName("自定义线程");
// 设置线程优先级(1-10,默认5)
thread.setPriority(Thread.MAX_PRIORITY);
// 设置为守护线程(可选)
thread.setDaemon(true);
// 启动线程
thread.start();
// 主线程信息
System.out.println("主线程名称: " + Thread.currentThread().getName());
System.out.println("主线程优先级: " + Thread.currentThread().getPriority());
try {
Thread.sleep(5000); // 主线程等待5秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程结束");
}
}
守护线程详解
守护线程特点
- 后台服务:为其他线程提供服务的后台线程
- 自动结束:当所有非守护线程结束时,守护线程自动结束
- JVM退出:JVM不会等待守护线程执行完成
- 典型应用:垃圾回收器、定时器等
DaemonThreadExample.java
public class DaemonThreadExample {
public static void main(String[] args) {
Thread daemonThread = new Thread(() -> {
while (true) {
System.out.println("守护线程正在运行...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("守护线程被中断");
return;
}
}
});
// 设置为守护线程
daemonThread.setDaemon(true);
daemonThread.start();
// 主线程执行一段时间后结束
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程结束,守护线程也会自动结束");
}
}
线程异常处理
线程中的异常不能被主线程捕获,需要使用特殊的异常处理机制。Java提供了UncaughtExceptionHandler接口来处理线程中的未捕获异常。
异常处理示例
ThreadExceptionExample.java
public class ThreadExceptionExample {
public static void main(String[] args) {
// 设置全局异常处理器
Thread.setDefaultUncaughtExceptionHandler((thread, exception) -> {
System.err.println("线程 " + thread.getName() + " 发生异常: " + exception.getMessage());
exception.printStackTrace();
});
Thread thread1 = new Thread(() -> {
System.out.println("线程1开始执行");
// 故意制造异常
int result = 10 / 0;
}, "异常线程1");
Thread thread2 = new Thread(() -> {
System.out.println("线程2开始执行");
try {
Thread.sleep(2000);
System.out.println("线程2正常结束");
} catch (InterruptedException e) {
System.out.println("线程2被中断");
}
}, "正常线程2");
// 为特定线程设置异常处理器
thread1.setUncaughtExceptionHandler((thread, exception) -> {
System.err.println("特定处理器: 线程 " + thread.getName() + " 异常: " + exception.getClass().getSimpleName());
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程结束");
}
}
异常处理最佳实践
预防异常
- 在线程代码中使用try-catch
- 验证输入参数
- 处理可能的空指针
异常记录
- 使用日志框架记录异常
- 包含线程名称和时间戳
- 记录异常堆栈信息
异常恢复
- 实现重试机制
- 优雅降级处理
- 通知监控系统