杰瑞科技汇

Java如何实现Runnable接口?

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

Java如何实现Runnable接口?-图1
(图片来源网络,侵删)

什么是 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 是单继承的)。

步骤:

  1. 创建一个类,并让它 implements Runnable
  2. 重写 run() 方法,将你想要在新线程中执行的代码放入其中。
  3. 创建 Runnable 接口的实例(也就是你创建的那个类的对象)。
  4. 创建 Thread 类的实例,并将上一步创建的 Runnable 实例作为构造参数传入。
  5. 调用 thread.start() 方法来启动线程。注意:是 start(),不是 run()

代码示例:

Java如何实现Runnable接口?-图2
(图片来源网络,侵删)
// 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 实例,无需定义一个单独的类。

*语法:`() -> { / 你的代码 */ }`

Java如何实现Runnable接口?-图3
(图片来源网络,侵删)

代码示例:

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

在这个例子中,AliceBob 两个线程操作的是同一个 BankAccount 对象,实现了资源共享,如果使用继承 Thread 的方式,实现起来会麻烦得多。

  • 实现 Runnable 是 Java 中创建线程的标准且推荐的方式。
  • 它通过将任务逻辑 (run() 方法) 与线程创建 (Thread 类) 分离,提供了更好的灵活性和设计。
  • 结合 Lambda 表达式,可以使代码变得非常简洁。
  • 当多个线程需要操作共享数据时,Runnable 是实现这一目标的理想选择。
分享:
扫描分享到社交APP
上一篇
下一篇