ZetCode

Java Runnable 接口

最后修改时间:2025 年 4 月 13 日

java.lang.Runnable 接口是 Java 并发模型的基础组成部分。 它代表一个可以由线程执行的任务。 任何需要由线程执行的类的实例都应实现此接口。

Runnable 接口仅包含一个方法:run。 当使用实现 Runnable 接口的对象创建线程时,启动线程会导致在该单独执行的线程中调用该对象的 run 方法。 这提供了一种定义应在线程中运行的代码的方式。

Runnable 接口定义

Runnable 接口非常简单,仅包含一个抽象方法。 这使其成为一个函数式接口,这意味着它可以与 Java 8 及更高版本中的 Lambda 表达式一起使用。 这是它的定义:

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

上面的代码显示了 Runnable 接口的完整定义。 @FunctionalInterface 注解表示它可以与 Lambda 表达式一起使用。 该接口只需要实现 run 方法。

基本的 Runnable 实现

此示例演示了通过创建一个实现 Runnable 接口的类来实现该接口的最基本方法。 然后,我们使用 Runnable 创建一个 Thread 对象并启动它。

Main.java
package com.zetcode;

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Thread is running...");
        for (int i = 1; i <= 5; i++) {
            System.out.println("Count: " + i);
            try {
                Thread.sleep(500); // Pause for 500 milliseconds
            } catch (InterruptedException e) {
                System.out.println("Thread interrupted");
            }
        }
        System.out.println("Thread finished.");
    }
}

public class Main {

    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
        
        System.out.println("Main thread continues to run...");
    }
}

在此示例中,我们创建一个实现 Runnable 接口的 MyRunnable 类。 run 方法包含将在新线程中执行的代码。 我们使用 Runnable 实例创建一个 Thread 对象,并使用 thread.start 启动它。 主线程继续执行,而新线程并发运行。

带有 Lambda 表达式的 Runnable

使用 Java 8 的 Lambda 表达式,我们可以更简洁地实现 Runnable,而无需创建单独的类。 这是可能的,因为 Runnable 是一个仅包含一个抽象方法的函数式接口。

Main.java
package com.zetcode;

public class Main {

    public static void main(String[] args) {
        // Using lambda expression to implement Runnable
        Runnable runnable = () -> {
            System.out.println("Lambda thread running...");
            for (int i = 0; i < 3; i++) {
                System.out.println("Working: " + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    System.out.println("Interrupted!");
                }
            }
            System.out.println("Lambda thread done.");
        };
        
        Thread thread = new Thread(runnable);
        thread.start();
        
        System.out.println("Main thread doing other work...");
    }
}

此示例显示了如何使用 Lambda 表达式来实现 Runnable。 Lambda 提供了一种更简洁的方式来定义线程的行为,而无需创建单独的类。 语法 () -> { ... } 实现了 Runnable 接口的单个 run 方法。

使用 Runnable 的多个线程

我们可以使用相同的 Runnable 实例创建多个线程。 当我们希望多个线程执行相同的任务时,这非常有用。 每个线程将具有自己的执行路径。

Main.java
package com.zetcode;

class CounterRunnable implements Runnable {
    private final String name;
    
    public CounterRunnable(String name) {
        this.name = name;
    }
    
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(name + ": " + i);
            try {
                Thread.sleep((long)(Math.random() * 1000));
            } catch (InterruptedException e) {
                System.out.println(name + " interrupted");
            }
        }
        System.out.println(name + " finished.");
    }
}

public class Main {

    public static void main(String[] args) {
        Runnable counter1 = new CounterRunnable("Counter 1");
        Runnable counter2 = new CounterRunnable("Counter 2");
        
        Thread thread1 = new Thread(counter1);
        Thread thread2 = new Thread(counter2);
        
        thread1.start();
        thread2.start();
        
        System.out.println("Main thread waiting for counters to finish...");
    }
}

在此示例中,我们创建了两个线程,它们共享相同的 Runnable 实现,但具有不同的名称。 每个线程使用随机睡眠间隔计数到 5,显示线程如何并发执行。 输出将显示来自两个线程的交错计数,展示了并发执行。

带有匿名类的 Runnable

在 Java 8 引入 Lambda 之前,匿名类通常用于简洁地实现 Runnable。 这种方法仍然有效,并且在需要访问封闭方法中的局部变量时非常有用。

Main.java
package com.zetcode;

public class Main {

    public static void main(String[] args) {
        final String message = "Hello from thread!";
        
        // Using anonymous class to implement Runnable
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(message);
                for (int i = 0; i < 3; i++) {
                    System.out.println("Processing step " + (i + 1));
                    try {
                        Thread.sleep(800);
                    } catch (InterruptedException e) {
                        System.out.println("Processing interrupted");
                    }
                }
                System.out.println("Processing complete.");
            }
        });
        
        thread.start();
        
        System.out.println("Main thread continues execution...");
    }
}

此示例演示了使用匿名类实现 Runnable。 匿名类可以访问 message 变量,因为它被声明为 final。 这种方法比 Lambda 更冗长,但在使用 Java 8 之前的版本或需要覆盖多个方法时是必需的。

带有方法引用的 Runnable

当您有一个与 Runnable 的 run 方法签名匹配的现有方法时,Java 8 方法引用提供了另一种简洁的实现 Runnable 的方法。 这对于不带参数且返回 void 的现有方法特别有用。

Main.java
package com.zetcode;

public class Main {
    public static void workerMethod() {
        System.out.println("Worker method executing in thread: " + 
                         Thread.currentThread().getName());
        for (int i = 0; i < 4; i++) {
            System.out.println("Working on task " + (i + 1));
            try {
                Thread.sleep(600);
            } catch (InterruptedException e) {
                System.out.println("Work interrupted");
            }
        }
        System.out.println("Worker method completed.");
    }
    
    public static void main(String[] args) {
        // Using method reference to implement Runnable
        Thread thread = new Thread(Main::workerMethod);
        thread.start();
        
        System.out.println("Main thread doing other tasks...");
    }
}

在此示例中,我们使用方法引用 Main::workerMethod 来实现 Runnable。 workerMethod 匹配 run 的签名(没有参数,void 返回),因此它可以直接用作 Runnable。 这种方法简洁明了,并且在您具有适合 Runnable 接口的现有方法时效果很好。

带有线程池的 Runnable

在实际应用中,通常最好使用线程池而不是直接创建线程。 Executor 框架提供了可以有效管理 Runnable 任务的线程池实现。

Main.java
package com.zetcode;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class Task implements Runnable {
    private final int taskId;
    
    public Task(int id) {
        this.taskId = id;
    }
    
    @Override
    public void run() {
        System.out.println("Starting task " + taskId + 
                          " in thread: " + Thread.currentThread().getName());
        try {
            // Simulate work
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            System.out.println("Task " + taskId + " interrupted");
        }
        System.out.println("Completed task " + taskId);
    }
}

public class Main {

    public static void main(String[] args) {
        // Create a thread pool with 3 threads
        ExecutorService executor = Executors.newFixedThreadPool(3);
        
        // Submit 10 tasks to the pool
        for (int i = 1; i <= 10; i++) {
            Runnable task = new Task(i);
            executor.execute(task);
        }
        
        // Shutdown the executor when done
        executor.shutdown();
        System.out.println("All tasks submitted to thread pool.");
    }
}

此示例演示了将 Runnable 与线程池一起使用。 我们创建一个包含 3 个线程的池,并向其提交 10 个任务。 该池有效地管理任务执行,重用线程而不是为每个任务创建新线程。 请注意,只有 3 个任务并发运行,其他任务在队列中等待直到有线程可用。

Runnable 与 Thread

虽然 Runnable 和 Thread 都可以用于创建线程,但通常首选 Runnable,因为它将任务与线程执行机制分离。 这允许更灵活的设计,其中相同的任务可以以不同的方式执行。

Main.java
package com.zetcode;

// Approach 1: Extending Thread
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread approach: " + getName());
    }
}

// Approach 2: Implementing Runnable
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable approach: " + 
                         Thread.currentThread().getName());
    }
}

public class Main {

    public static void main(String[] args) {
        // Using Thread subclass
        MyThread thread1 = new MyThread();
        thread1.start();
        
        // Using Runnable
        Thread thread2 = new Thread(new MyRunnable());
        thread2.start();
        
        // Using lambda with Runnable
        Thread thread3 = new Thread(() -> 
            System.out.println("Lambda Runnable: " + 
                             Thread.currentThread().getName()));
        thread3.start();
    }
}

此示例比较了在 Java 中创建线程的两种方法。 Runnable 方法更灵活,因为它允许您的类在需要时扩展另一个类。 它还可以更好地与现代 Java 功能(如 Lambda 和线程池)配合使用。 Thread 方法更简单,但灵活性较差,因为 Java 不支持多重继承。

来源

Java Runnable 接口文档

在本教程中,我们通过实际示例深入探讨了 Java Runnable 接口。 Runnable 是 Java 并发模型的基础,理解它对于在 Java 中编写多线程应用程序至关重要。

作者

我叫 Jan Bodnar,是一位拥有多年经验的敬业程序员。 我于 2007 年开始撰写编程文章,此后撰写了超过 1,400 篇文章和八本电子书。 凭借超过八年的教学经验,我致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程