下面我将从基本概念、实现步骤、完整代码示例、以及与 Thread 类的对比几个方面来详细解释。

什么是 Runnable?
Runnable 是 Java 中一个接口,位于 java.lang 包中,它只包含一个方法:
public interface Runnable {
public abstract void run();
}
- 作用:
Runnable代表一个“任务”或者“一段可以被执行的代码”。 - 核心:
run()方法是任务的主体,当线程启动时,JVM 会调用这个方法。 - 重要:
run()方法不是public void run()吗?为什么说它不是启动线程的入口?关键点在于:直接调用myRunnable.run()只是在当前线程中执行了这段代码,并不会创建新的线程,必须通过Thread类来启动它,才能真正实现并发。
如何实现 Runnable(两种主要方式)
传统类实现(实现 Runnable 接口)
这是最传统的方式,适合需要继承其他类的情况(因为 Java 是单继承的)。
步骤:
- 创建一个类,并让它
implements Runnable。 - 重写
run()方法,将你想要在新线程中执行的代码放入其中。 - 创建
Runnable接口的实例(也就是你创建的那个类的对象)。 - 创建
Thread类的实例,并将上一步创建的Runnable实例作为构造参数传入。 - 调用
thread.start()方法来启动线程。注意:是start(),不是run()!
代码示例:

// 1. 创建一个类,实现 Runnable 接口
class MyTask implements Runnable {
private String taskName;
public MyTask(String name) {
this.taskName = name;
}
// 2. 重写 run() 方法
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println(taskName + " is running, count: " + i);
try {
// 模拟任务执行,睡眠 500 毫秒
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(taskName + " has finished.");
}
}
public class RunnableDemo {
public static void main(String[] args) {
// 3. 创建 Runnable 实例
MyTask task1 = new MyTask("Task-A");
MyTask task2 = new MyTask("Task-B");
// 4. 创建 Thread 实例,并将 Runnable 实例传入
Thread thread1 = new Thread(task1);
Thread thread2 = new Thread(task2);
// 5. 启动线程
System.out.println("Starting threads...");
thread1.start();
thread2.start();
System.out.println("Main thread continues its work...");
}
}
可能的输出结果(顺序可能因线程调度而异):
Starting threads...
Main thread continues its work...
Task-A is running, count: 1
Task-B is running, count: 1
Task-A is running, count: 2
Task-B is running, count: 2
...
Task-A has finished.
Task-B has finished.
可以看到,主线程和两个新线程是并发执行的。
使用 Lambda 表达式(现代、简洁)
从 Java 8 开始,Runnable 成了一个函数式接口(只有一个抽象方法),因此我们可以使用 Lambda 表达式来更简洁地创建 Runnable 实例,无需定义一个单独的类。
*语法:`() -> { / 你的代码 */ }`

代码示例:
public class LambdaRunnableDemo {
public static void main(String[] args) {
// 使用 Lambda 表达式直接创建 Runnable 实例
Runnable task1 = () -> {
for (int i = 1; i <= 3; i++) {
System.out.println("Lambda Task 1 is running, count: " + i);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Lambda Task 1 has finished.");
};
Runnable task2 = () -> {
for (int i = 1; i <= 3; i++) {
System.out.println("Lambda Task 2 is running, count: " + i);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Lambda Task 2 has finished.");
};
// 创建并启动线程
Thread thread1 = new Thread(task1);
Thread thread2 = new Thread(task2);
thread1.start();
thread2.start();
System.out.println("Main thread continues its work...");
}
}
这种方式代码更短,可读性更高,尤其适合简单的、一次性的任务。
Runnable vs. 继承 Thread 类
这是初学者常有的疑问,两者都可以创建线程,但有本质区别。
| 特性 | 实现 Runnable 接口 |
继承 Thread 类 |
|---|---|---|
| 类继承 | 一个类可以实现多个接口,灵活性高。 | Java 是单继承的,继承了 Thread 后就不能再继承其他类了。 |
| 资源共享 | 非常适合,多个线程可以共享同一个 Runnable 实例,从而共享其实例变量。 |
不方便,每个线程都是 Thread 类的一个新实例,共享数据需要额外处理(如使用 static 变量)。 |
| 设计模式 | 更符合“面向接口编程”和“组合优于继承”的设计原则,将“任务”和“执行机制”解耦。 | 将任务和线程本身耦合在一起,设计上不够灵活。 |
| 代码简洁性 | 结合 Lambda 表达式,代码非常简洁。 | 定义一个新类来继承 Thread,代码量相对较多。 |
优先选择实现 Runnable 接口,它更灵活、更符合现代 Java 的设计理念。
一个关于共享资源的例子
Runnable 的一个巨大优势是方便实现资源共享,假设我们有一个银行账户,两个线程同时向其存钱。
// 共享资源类
class BankAccount {
private int balance = 0;
public void deposit(int amount) {
this.balance += amount;
System.out.println(Thread.currentThread().getName() + " deposited. Current balance: " + this.balance);
}
}
// 任务类
class DepositTask implements Runnable {
private BankAccount account;
private int amount;
public DepositTask(BankAccount account, int amount) {
this.account = account;
this.amount = amount;
}
@Override
public void run() {
// 每个线程存钱3次
for (int i = 0; i < 3; i++) {
account.deposit(amount);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class SharedResourceDemo {
public static void main(String[] args) {
// 1. 创建一个共享的 BankAccount 对象
BankAccount account = new BankAccount();
// 2. 创建两个任务,但它们共享同一个 account 对象
DepositTask task1 = new DepositTask(account, 100);
DepositTask task2 = new DepositTask(account, 200);
// 3. 创建两个线程来执行这两个任务
Thread thread1 = new Thread(task1, "Alice");
Thread thread2 = new Thread(task2, "Bob");
thread1.start();
thread2.start();
}
}
输出(可能):
Alice deposited. Current balance: 100
Bob deposited. Current balance: 300
Alice deposited. Current balance: 400
Bob deposited. Current balance: 600
Alice deposited. Current balance: 700
Bob deposited. Current balance: 900
在这个例子中,Alice 和 Bob 两个线程操作的是同一个 BankAccount 对象,实现了资源共享,如果使用继承 Thread 的方式,实现起来会麻烦得多。
- 实现
Runnable是 Java 中创建线程的标准且推荐的方式。 - 它通过将任务逻辑 (
run()方法) 与线程创建 (Thread类) 分离,提供了更好的灵活性和设计。 - 结合 Lambda 表达式,可以使代码变得非常简洁。
- 当多个线程需要操作共享数据时,
Runnable是实现这一目标的理想选择。
