继承 Thread 类创建线程

Java 使用 Thread 类代表线程,所有的线程对象都必须是 Thread 类或其子类的实例。

可以通过继承 Thread 类来创建线程,一般步骤如下:

  • 定义一个子类(本例中命名为 ThreadByThread),该类继承 Thread 类并重写 run()方法,run() 方法体即为线程需要完成的任务
  • 创建该子类的示例,即创建线程对象
  • 调用该线程对象的 start() 方法来启动线程

示例代码如下:

// ThreadTest.java
package cn.imztj.test.thread;

public class ThreadTest {
    public static void main(String[] args) {
        new ThreadByThread().start();
    }
}

class ThreadByThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("Current Thread: " + Thread.currentThread().getName() + ", Current i: " + i);
        }
    }
}

测试结果如下,可以看到,两个线程交替执行:

Current Thread: Thread-1, Current i: 0
Current Thread: Thread-0, Current i: 0
Current Thread: Thread-1, Current i: 1
Current Thread: Thread-0, Current i: 1
Current Thread: Thread-1, Current i: 2
Current Thread: Thread-0, Current i: 2
Current Thread: Thread-1, Current i: 3
Current Thread: Thread-0, Current i: 3
Current Thread: Thread-1, Current i: 4
Current Thread: Thread-0, Current i: 4

实现 Runnable 接口创建线程

  • 首先,我们定义一个 Runnable 接口的实现类(本例中命名为 ThreadByRunnable),并重写该接口的 run() 方法,run() 方法体即为线程需要完成的任务
  • 创建该实现类的实例,并用这个实例作为 Thread 的 target 来创建线程对象
  • 通过调用线程对象的 start() 方法来启动线程
// RunnableTest.java
package cn.imztj.test.thread;

public class RunnableTest {
    public static void main(String[] args) {
        ThreadByRunnable threadByRunnable = new ThreadByRunnable();
        new Thread(threadByRunnable, "线程 1").start();
        new Thread(threadByRunnable, "线程 2").start();
    }
}

class ThreadByRunnable implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("Current Thread: " + Thread.currentThread().getName() + ", Current i: " + i);
        }
    }
}

测试结果如下,可以看到两个线程交替执行,在本例中,我们还对线程进行了命名:

Current Thread: 线程 2, Current i: 0
Current Thread: 线程 1, Current i: 0
Current Thread: 线程 2, Current i: 1
Current Thread: 线程 1, Current i: 1
Current Thread: 线程 2, Current i: 2
Current Thread: 线程 1, Current i: 2
Current Thread: 线程 2, Current i: 3
Current Thread: 线程 1, Current i: 3
Current Thread: 线程 2, Current i: 4
Current Thread: 线程 1, Current i: 4

利用 Callable 和 Future 创建线程

与 Runnable 接口不同,Callable 接口提供了一个 call() 方法作为线程执行体,与 Runnable 接口的 run() 方法相比,call() 方法可以有返回值,可以声明抛出异常,而 run() 方法则不行。

Callable 接口在 JUC (java.util.concurrent) 包下,其定义如下:

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V `call()` throws Exception;
}

Callable 接口不是 Runnable 接口的子接口,Callable 对象是不能直接作为 Thread 对象的 target 的。针对这个问题,Java5 提供了 Future 接口来接收 Callable 接口中 call() 方法的返回值,Java 还引入了 RunnableFuture 接口,它是 Runnable 接口和 Future 接口的子接口,可以作为 Thread 对象的 target。同时提供了一个 RunnableFuture 接口的实现类:FutureTask ,FutureTask 可以作为 Thread 对象的 target。

我们可以在 IDEA 中查看上述类和接口的关系图:

使用 Callable 和 Future 创建线程的步骤如下:

  • 定义一个类实现 Callable 接口,并重写 call() 方法,call() 方法即为线程执行体,有返回值,且可以声明异常。
  • 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象
  • 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动线程
  • 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值
// CallableTest.java
package cn.imztj.test.thread;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class CallableTest {
    public static void main(String[] args) {
        ThreadByCallable threadByCallable = new ThreadByCallable();
        FutureTask<String> futureTask1 = new FutureTask<>(threadByCallable);
        FutureTask<String> futureTask2 = new FutureTask<>(threadByCallable);
        new Thread(futureTask1, "线程 1").start();
        new Thread(futureTask2, "线程 2").start();
        try {
            System.out.println("线程 1 的返回值:" + futureTask1.get());
            System.out.println("线程 2 的返回值:" + futureTask2.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class ThreadByCallable implements Callable {

    @Override
    public Object `call()` throws Exception {
        for (int i = 0; i < 5; i++) {
            System.out.println("Current Thread: " + Thread.currentThread().getName() + ", Current i: " + i);
        }
        return Thread.currentThread().getName();
    }
}

执行结果如下:

Current Thread: 线程 1, Current i: 0
Current Thread: 线程 2, Current i: 0
Current Thread: 线程 1, Current i: 1
Current Thread: 线程 2, Current i: 1
Current Thread: 线程 1, Current i: 2
Current Thread: 线程 2, Current i: 2
Current Thread: 线程 1, Current i: 3
Current Thread: 线程 2, Current i: 3
Current Thread: 线程 1, Current i: 4
Current Thread: 线程 2, Current i: 4
线程 1 的返回值:线程 1
线程 2 的返回值:线程 2

三种创建方式的对比

采用继承 Thread 类方式,优点是代码编写上比较简单,如果需要访问当前线程,无需使用 Thread.currentThread() 方法,可以直接使用 this。缺点是,由于已经继承了 Thread 类,所以它将不能再继承其他的父类。

采用实现 Runnable 接口方式,优点是线程类还可以继承其他的类,而且在这种方式下,多个线程可以共享同一个目标对象,适合多个相同线程来处理同一份资源。在这种情况下,如果需要访问当前线程,必须使用 Thread.currentThread() 方法。

采用实现 Callable 接口的方式,同样可以可以避免 Java 中单继承的限制,适合多个线程进行资源共享。这种方式还可以获得一个 Future 对象,通过 Future 对象可以得到异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果,也可以取消任务的执行。

另外,实际开发中应避免显式创建线程,而是建议使用线程池,而线程池只能放入 Runable 或 Callable 接口实现类,不能直接放入继承 Thread 的类。