继承 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 的类。