Java 使用 Thread 类代表线程,所有的线程对象都必须是 Thread 类或其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流(一段顺序执行的代码)。Java 使用线程执行体来代表这段程序流。
本文包含:
- 继承Thread 类创建线程类
- 实现Runnable 接口创建线程类
- 使用 Callable 和 Future 创建线程
- 创建线程的三种方式对比
1. 继承Thread 类创建线程类
通过继承 Thread 类来创建并启动多线程的步骤如下。
下面程序示范了通过继承 Thread 类来创建并启动多线程。
下面为其运行结果图:
运行多线程编程时不要忘了 Java 程序运行时默认的主线程,main() 方法的方法体就是主线程的线程执行体。
此外,上面程序还用到了线程的如下两个方法。
Thread-0 和 Thread-1 两个线程输出的 i 变量不连续—注意: i 变量是 FirstThread 的实例变量,而不是局部变量,但因为程序每次创建线程对象时都需要创建一个 FirstThread 对象,所以 Thread-0 和 Thread-1 不能共享该实例变量。
注意:
使用 继承Thread 类的方法来创建线程类时,多个线程之间无法共享线程类的实例变量。
2. 实现Runnable 接口创建线程类
实现 Runnable 接口来创建并启动多线程的步骤如下:
采用 Runnable 接口的方式创建的多线程可以共享线程类的实例变量。这是因为在这种方式下,程序所创建的 Runnable 对象只是线程的 target ,而多个线程可以共享一个 target,所以多个线程可以共享一个线程类(实际上应该是线程的 target 类)的实例变量。
3. 使用 Callable 和 Future 创建线程
通过实现 Runnable 接口创建多线程时,Thread 类的作用就是把 run() 方法包装成线程执行体。那么是否可以把任意方法都包装成线程执行体呢?Java 目前不行。
从 Java 5 开始,Java 提供了Callable 接口,该接口怎么看都像是Runnable 接口的增强版,Callable 接口提供了一个call() 方法可以作为线程执行体,但call() 得到比 run() 方法功能更强大。
call() 方法可以有返回值
call() 方法可以声明抛出异常
在Future 接口里定义了如下几个公共方法来控制它关联的 Callable 任务。
Callable 接口有泛型限制,Callable 接口里的泛型形参类型与 call() 方法返回值类型相同。而且 Callable 接口是函数式接口,因此可以使用 Lambda 表达式创建 Callable 对象。
创建并启动有返回值的线程的步骤如下:
下面程序通过实现 Callable 接口来实现线程类,并启动该线程。
4. 创建线程的三种方式对比
采用实现Runnable、Callable 接口的方式创建多线程的优缺点:
采用继承 Thread 类的方式创建多线程的优缺点:
鉴于上面分析,因此一般推荐采用实现 Runnable 接口、Callable 接口的方式来创建多线程。