理解 Runnable 接口的作用及其在多线程编程中的核心地位
掌握通过实现 Runnable 创建线程的标准步骤
能够使用 Lambda 表达式简化 Runnable 的编写
了解如何结合 Executor 框架高效管理线程任务
掌握 Runnable 中异常处理的正确方式
明确 Runnable 与直接继承 Thread 类的优劣对比
Runnable 是 Java 标准库 java.lang 包中的一个函数式接口,用于定义一个可以被线程执行的任务。它将任务逻辑(做什么)与线程执行机制(如何执行)解耦,是实现多线程的推荐方式。
接口声明
public interface Runnable {
void run();
}只包含一个抽象方法 run()
run() 方法中编写线程要执行的具体代码
由于只有一个抽象方法,Runnable 是典型的函数式接口,支持 Lambda 表达式
Java 不支持多重继承。如果一个类已经继承了其他父类(如 JFrame、Exception 等),就无法再继承 Thread 类。而实现 Runnable 接口则没有此限制,同时还能保持良好的面向对象设计:
职责分离:任务逻辑与线程控制分离
更高的灵活性和复用性
符合“组合优于继承”的设计原则
创建一个类实现 Runnable 接口,并重写 run() 方法:
class DataProcessor implements Runnable {
@Override
public void run() {
System.out.println("数据处理任务正在运行...");
// 模拟耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("数据处理完成");
}
}将 Runnable 实例传给 Thread 构造器,调用 start() 启动新线程:
public class MainApp {
public static void main(String[] args) {
DataProcessor task = new DataProcessor();
Thread worker = new Thread(task);
worker.start(); // 启动新线程执行 run()
}
}调用
start()会创建新线程并自动调用run();若直接调用task.run(),则仍在主线程中同步执行,不会开启新线程。
由于 Runnable 是函数式接口,可用 Lambda 表达式替代匿名内部类或独立类:
public class LambdaDemo {
public static void main(String[] args) {
Runnable task = () -> {
System.out.println("使用 Lambda 执行的线程任务");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Lambda 任务结束");
};
new Thread(task).start();
}
}输出:
使用 Lambda 执行的线程任务
Lambda 任务结束这种写法简洁、直观,特别适合一次性、轻量级任务。
手动创建 Thread 对象效率低且难以管理。Java 提供了 Executor 框架用于线程池管理:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorDemo {
public static void main(String[] args) {
// 创建固定大小为 2 的线程池
ExecutorService executor = Executors.newFixedThreadPool(2);
// 提交多个任务
for (int i = 0; i < 5; i++) {
final int taskId = i;
executor.execute(() -> {
System.out.println("任务 " + taskId + " 由线程 "
+ Thread.currentThread().getName() + " 执行");
});
}
// 关闭线程池(不再接受新任务,但会执行完已提交任务)
executor.shutdown();
}
}优势:
自动复用线程,减少创建 / 销毁开销
控制并发数量,防止资源耗尽
更高级的调度策略(如定时、周期性任务)
run() 方法不能抛出检查型异常(因其签名未声明 throws),所有异常必须在方法内部处理:
class RiskyTask implements Runnable {
@Override
public void run() {
try {
// 可能抛出运行时异常的操作
int result = 10 / 0;
} catch (ArithmeticException e) {
System.err.println("捕获到算术异常: " + e.getMessage());
// 记录日志、回滚状态等
} catch (Exception e) {
System.err.println("未知异常: " + e.getMessage());
}
// 若不捕获,线程会静默终止!
}
}
public class ExceptionDemo {
public static void main(String[] args) {
new Thread(new RiskyTask()).start();
}
}未捕获的异常会导致线程直接终止,且不会传播到主线程,容易造成“静默失败”。
Runnable 是 Java 多线程编程的标准任务模型
必须通过 Thread 或 Executor 来执行 Runnable 任务
run() 方法不能抛出检查型异常,需内部处理所有异常
Lambda 表达式可极大简化 Runnable 的编写
在生产环境中应优先使用 ExecutorService 管理线程,而非手动创建 Thread
实现 Runnable 比继承 Thread 更符合面向对象设计原则
如果一个类已经继承了某个父类,是否还能使用多线程?如何实现?
为什么 run() 方法不能声明抛出检查型异常?这对程序设计有何影响?
在 Web 应用中,为什么通常不建议直接使用 new Thread().start(),而要使用线程池?