创建和启动 Java 线程

更新于 2025-12-28

Jakob Jenkov 2024-08-06

Java 线程就像是一个虚拟的 CPU,可以在你的 Java 应用程序内部执行代码。当一个 Java 应用程序启动时,它的 main() 方法由主线程(main thread)执行——这是由 Java 虚拟机(JVM)创建的一个特殊线程,用于运行你的应用程序。在应用程序内部,你还可以创建并启动更多线程,这些线程可以与主线程并行执行你应用程序的部分代码。

Java 线程和其他 Java 对象一样,也是对象。它们是 java.lang.Thread 类的实例,或者是该类的子类实例。除了作为对象之外,Java 线程还能执行代码。在本教程中,我将解释如何创建和启动线程。

创建和启动线程

在 Java 中创建线程的方式如下:

Thread thread = new Thread();

要启动这个 Java 线程,你需要调用它的 start() 方法:

thread.start();

上面这个例子没有为线程指定任何要执行的代码,因此线程在启动后会立即停止。

有两种方式可以指定线程要执行的代码:

  1. 创建 Thread 的子类并重写 run() 方法。
  2. Thread 构造函数传入一个实现了 Runnablejava.lang.Runnable)接口的对象。

下面将分别介绍这两种方法。


Thread 子类

第一种指定线程执行代码的方式是创建 Thread 的子类,并重写 run() 方法。start() 被调用后,线程会执行 run() 方法中的代码。

下面是一个创建 Java Thread 子类的例子:

public class MyThread extends Thread {
    public void run(){
        System.out.println("MyThread running");
    }
}

要创建并启动上述线程,可以这样做:

MyThread myThread = new MyThread();
myThread.start();

调用 start() 后会立即返回,不会等待 run() 方法执行完毕。run() 方法的执行就像由另一个 CPU 执行一样。当 run() 方法执行时,它会打印出文本 "MyThread running"

你也可以创建一个匿名的 Thread 子类:

Thread thread = new Thread(){
    public void run(){
        System.out.println("Thread Running");
    }
};
thread.start();

这个例子会在新线程执行 run() 方法时打印出 "Thread running"


实现 Runnable 接口

第二种指定线程执行代码的方式是创建一个实现 java.lang.Runnable 接口的类。实现了 Runnable 接口的 Java 对象可以被 Java Thread 执行(具体方法稍后说明)。

Runnable 接口是 Java 平台自带的标准接口,它只有一个方法 run()。其基本结构如下:

public interface Runnable {
    public void run();
}

线程需要执行的所有逻辑都应包含在 run() 方法的实现中。有三种方式可以实现 Runnable 接口:

  1. 创建一个实现 Runnable 接口的 Java 类。
  2. 创建一个实现 Runnable 接口的匿名类。
  3. 使用 Java Lambda 表达式实现 Runnable 接口。

下面将逐一介绍这三种方式。

Java 类实现 Runnable

第一种方式是创建一个自定义的 Java 类来实现 Runnable 接口。例如:

public class MyRunnable implements Runnable {
    public void run(){
        System.out.println("MyRunnable running");
    }
}

这个 Runnable 实现只是简单地打印出 "MyRunnable running"。打印完成后,run() 方法退出,执行该方法的线程也随之终止。

匿名类实现 Runnable

你也可以使用匿名类来实现 Runnable 接口:

Runnable myRunnable = new Runnable(){
    public void run(){
        System.out.println("Runnable running");
    }
};

除了使用匿名类外,这个例子与前面使用自定义类的方式非常相似。

使用 Lambda 表达式实现 Runnable

第三种方式是使用 Java Lambda 表达式 来实现 Runnable 接口。之所以可行,是因为 Runnable 接口只有一个未实现的方法,因此它实际上(即使不是有意设计成)是一个函数式接口

Lambda 表达式示例:

Runnable runnable = () -> {
    System.out.println("Lambda Runnable running");
};

使用 Runnable 启动线程

要让 run() 方法由线程执行,只需将实现了 Runnable 接口的类、匿名类或 Lambda 表达式的实例传递给 Thread 的构造函数。例如:

Runnable runnable = new MyRunnable(); // 也可以是匿名类或 Lambda
Thread thread = new Thread(runnable);
thread.start();

线程启动后,会调用 MyRunnable 实例的 run() 方法,而不是执行 Thread 自己的 run() 方法。上述示例将打印出 "MyRunnable running"


子类 vs. Runnable?

两种方法没有绝对的优劣之分,都能正常工作。不过我个人更倾向于实现 Runnable 接口,并将其实例传递给 Thread。当你使用线程池执行 Runnable 任务时,可以轻松地将 Runnable 实例排队,直到线程池中有空闲线程可用。而使用 Thread 子类则较难做到这一点。

有时你可能需要同时实现 Runnable 并继承 Thread。例如,当你想创建一个能执行多个 Runnable 任务的 Thread 子类时(比如实现线程池),这种情况就会出现。


常见陷阱:调用 run() 而不是 start()

创建和启动线程时,一个常见错误是调用了 Threadrun() 方法,而不是 start() 方法,例如:

Thread newThread = new Thread(new MyRunnable());
newThread.run(); // 错!应该调用 start()

一开始你可能注意不到问题,因为 Runnablerun() 方法确实被执行了。但关键在于:它并不是由新创建的线程执行的,而是由创建该线程的线程(即调用上述两行代码的线程)执行的。

要让 MyRunnablerun() 方法由新线程 newThread 执行,必须调用 newThread.start()


线程名称

创建 Java 线程时,你可以为其指定一个名称。名称有助于区分不同的线程。例如,当多个线程向 System.out 输出内容时,能看到是哪个线程输出的信息会很有帮助。

示例:

Thread thread = new Thread("New Thread") {
    public void run(){
        System.out.println("run by: " + getName());
    }
};
thread.start();
System.out.println(thread.getName());

注意传给 Thread 构造函数的字符串 "New Thread",这就是线程的名称。可以通过 ThreadgetName() 方法获取该名称。

你也可以在使用 Runnable 实现时指定线程名称:

MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable, "New Thread");
thread.start();
System.out.println(thread.getName());

注意:由于 MyRunnable 类不是 Thread 的子类,因此它无法直接访问执行它的线程的 getName() 方法。


Thread.currentThread()

Thread.currentThread() 方法返回当前正在执行该方法的线程的 Thread 实例引用。通过这种方式,你可以获取到代表当前执行线程的 Thread 对象。

示例:

Thread thread = Thread.currentThread();

一旦获得 Thread 对象的引用,就可以调用其方法。例如,获取当前执行线程的名称:

String threadName = Thread.currentThread().getName();

Java 线程示例

下面是一个小例子。首先打印出执行 main() 方法的线程名称(这是 JVM 分配的主线程)。然后启动 10 个线程,并给每个线程分配一个数字作为名称("" + i)。每个线程都会打印自己的名称,然后停止执行。

public class ThreadExample {
    public static void main(String[] args){
        System.out.println(Thread.currentThread().getName());
        for(int i=0; i<10; i++){
            new Thread("" + i){
                public void run(){
                    System.out.println("Thread: " + getName() + " running");
                }
            }.start();
        }
    }
}

注意:尽管线程是按顺序启动的(1, 2, 3...),但它们的执行顺序不一定是顺序的。也就是说,线程 1 不一定是第一个向 System.out 输出名称的线程。这是因为线程本质上是并行执行的,而不是顺序执行的。线程的执行顺序由 JVM 和/或操作系统决定,不一定与启动顺序一致。


暂停线程

线程可以通过调用静态方法 Thread.sleep() 来暂停自身。sleep() 方法接收一个以毫秒为单位的参数,线程将尝试休眠指定的毫秒数后再恢复执行。虽然 Thread.sleep() 并非 100% 精确,但已经相当可靠。

示例:让线程暂停 10 秒(10,000 毫秒):

try {
    Thread.sleep(10L * 1000L);
} catch (InterruptedException e) {
    e.printStackTrace();
}

停止线程

停止 Java 线程需要在编写线程代码时做一些准备。Thread 类虽然有一个 stop() 方法,但已被弃用。因为原来的 stop() 方法无法保证线程在什么状态下被停止,这意味着线程访问的所有 Java 对象都可能处于未知状态。如果应用程序中的其他线程也能访问这些对象,程序可能会意外且不可预测地失败。

因此,不应调用 stop() 方法,而应自己实现线程的停止逻辑。

下面是一个实现 Runnable 的类,它包含一个额外的 doStop() 方法,用于向 Runnable 发出停止信号。Runnable 会检查该信号,并在合适的时候停止。

public class MyRunnable implements Runnable {
    private boolean doStop = false;

    public synchronized void doStop() {
        this.doStop = true;
    }

    private synchronized boolean keepRunning() {
        return this.doStop == false;
    }

    @Override
    public void run() {
        while(keepRunning()) {
            // 继续执行线程的工作
            System.out.println("Running");
            try {
                Thread.sleep(3L * 1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

注意 doStop()keepRunning() 方法。doStop() 应由其他线程调用(而非执行 run() 的线程),而 keepRunning() 则由执行 run() 的线程内部调用。只要 doStop() 未被调用,keepRunning() 就返回 true,线程就会继续运行。

启动并停止该线程的示例:

public class MyRunnableMain {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();

        try {
            Thread.sleep(10L * 1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        myRunnable.doStop();
    }
}

此示例首先创建 MyRunnable 实例,将其传给线程并启动。主线程休眠 10 秒后,调用 myRunnable.doStop(),从而导致执行 MyRunnable 的线程停止(因为 keepRunning() 将返回 false)。

注意:如果你的 Runnable 实现需要 run() 以外的方法(如 stop()pause()),那么就不能使用 Lambda 表达式来实现 Runnable,因为 Lambda 只能实现单个方法。此时必须使用自定义类,或者定义一个扩展了 Runnable 并包含额外方法的接口,并用匿名类实现它。


守护线程(Daemon Threads)

Java 中的守护线程(daemon thread)是指:当主线程退出应用程序时,守护线程不会阻止 JVM 关闭。而非守护线程则会保持 JVM 运行,即使主线程已经退出。

你可以通过 setDaemon(true) 方法将线程设置为守护线程:

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Daemon Thread running.");
    }
});
thread.setDaemon(true);
thread.start();

try {
    thread.join();
} catch (InterruptedException e) {
    throw new RuntimeException(e);
}

最后一行的 thread.join() 是为了确保主线程不会在守护线程执行完毕前退出(从而导致 JVM 提前关闭)。