跳转到内容

创建新线程

Java 语言内置了多线程支持。当 Java 程序启动的时候,实际上是启动了一个 JVM 进程,然后,JVM 启动主线程来执行 main() 方法。在 main() 方法中,又可以启动其他线程。

要创建一个新线程非常容易,需要实例化一个 Thread 实例,然后调用它的 start() 方法。

public class Main {
public static void main(String[] args) {
Thread t = new Thread();
t.start(); // 启动新线程
}
}

但是这个线程启动后实际上什么也不做就立刻结束了。我们希望新线程能执行指定的代码,有以下几种方法。

从 Thread 派生一个自定义类,然后覆写 run() 方法。

public class Main {
public static void main(String[] args) {
Thread t = new MyThread();
t.start(); // 启动新线程
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println("start new thread!");
}
}

执行上述代码,注意到 start() 方法会在内部自动调用实例的 run() 方法。

实现 Runnable 接口,创建 Thread 实例时,传入一个 Runnable 实例。

public class Main {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start(); // 启动新线程
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("start new thread!");
}
}

或者用 Java 8 引入的 lambda 语法进一步简写为:

public class Main {
public static void main(String[] args) {
Thread t = new Thread(() -> {
System.out.println("start new thread!");
});
t.start(); // 启动新线程
}
}

要特别注意:直接调用 Thread 实例的 run() 方法是无效的。

public class Main {
public static void main(String[] args) {
Thread t = new MyThread();
t.run(); // 错误用法
}
}
class MyThread extends Thread {
public void run() {
System.out.println("hello");
}
}

直接调用 run() 方法,相当于调用了一个普通的 Java 方法,当前线程并没有任何改变,也不会启动新线程。

上述代码实际上是在 main() 方法内部又调用了 run() 方法,打印 hello 语句是在 main 线程中执行的,没有任何新线程被创建。

必须调用 Thread 实例的 start() 方法才能启动新线程,如果我们查看 Thread 类的源代码,会看到 start() 方法内部调用了一个 private native void start0() 方法,native 修饰符表示这个方法是由 JVM 虚拟机内部的 C 代码实现的,不是由 Java 代码实现的。

3. 实现 Callable 接口(有返回值)

Section titled “3. 实现 Callable 接口(有返回值)”

前两种方式的 run() 方法没有返回值,也无法抛出受检异常。如果需要线程执行完后返回一个结果,可以使用 Callable 配合 FutureTask。

public class Main {
public static void main(String[] args) throws Exception {
FutureTask<Integer> task = new FutureTask<>(() -> {
System.out.println("start new thread!");
return 42; // 返回结果
});
Thread t = new Thread(task);
t.start(); // 启动新线程
Integer result = task.get(); // 阻塞,直到线程执行完毕
System.out.println("result: " + result); // 42
}
}
方式有无返回值能否抛出受检异常说明
继承 Thread无否占用唯一继承位,不推荐
实现 Runnable无否推荐,灵活,可配合线程池
实现 Callable有是需要返回值时使用

  1. Java 用 Thread 对象表示一个线程,通过调用 start() 启动一个新线程;
  2. 一个线程对象只能调用一次 start() 方法;
  3. 线程的执行代码写在 run() 方法中;
  4. 线程调度由操作系统决定,程序本身无法决定调度顺序;
  5. 需要有返回值时,使用 Callable + FutureTask,通过 get() 获取结果。

Q:继承 Thread 和实现 Runnable 有什么区别,推荐哪种? A:推荐实现 Runnable。继承 Thread 会占用唯一的继承位,而 Java 不支持多继承;实现 Runnable 更灵活,同一个 Runnable 实例可以被多个线程共享,也能方便地提交给线程池。

Q:start() 和 run() 的区别? A:start() 会向 JVM 申请创建一个新线程,并在新线程中执行 run();直接调用 run() 只是在当前线程中普通调用该方法,不会创建新线程。

Q:Callable 和 Runnable 的区别? A:Runnable 的 run() 无返回值、不能抛出受检异常;Callable 的 call() 有泛型返回值、可以抛出受检异常。需要获取线程执行结果时用 Callable,配合 FutureTask 或线程池的 submit() 使用。