创建线程
在 Java 中,主要提供两种方式实现线程,分别为继承 java.lang.Thread 类与实现 java.lang.Runnable 接口。本节将着重讲解这两种实现线程的方式。
继承Thread类
Thread 类是 java.lang 包中的一个类,从这个类中实例化的对象代表线程,程序员启动一个新线程需要建立 Thread 实例。Thread 类中常用的两个构造方法如下。
-
public Thread():创建一个新的线程对象。
-
public Thread(String threadName):创建一个名称为 threadName 的线程对象。
继承 Thread 类创建一个新的线程的语法如下:
public class ThreadTest extends Thread {
}
完成线程真正功能的代码放在类的 run() 方法中,当一个类继承 Thread 类后,就可以在该类中覆盖 run() 方法,将实现该线程功能的代码写入 run() 方法中,然后调用 Thread 类中的 start() 方法执行线程,也就是调用 run() 方法。
Thread 对象需要一个任务来执行,任务是指线程在启动时执行的工作,该工作的功能代码被写在 run() 方法中。run() 方法必须使用以下语法格式:
public void run() {
}
如果 start() 方法调用一个已经启动的线程,系统将抛出 IllegalThreadStateException 异常。 |
当执行一个线程程序时,就自动产生一个线程,主方法正是在这个线程上运行的。当不再启动其他线程时,该程序就为单线程程序,如本章以前的程序都是单线程程序。主方法线程启动由 Java 虚拟机负责,程序员负责启动自己的线程。代码如下:
public static void main(String[] args) {
new ThreadTest().start();
}
【例20.1】让线程循环输出1~10的数字(实例位置:资源包\TM\sl\20\1)
在项目中创建 ThreadTest 类并继承 Thread 类,在 run() 方法中编写代码,实现循环输出 10 个数字,然后启动线程。
public class ThreadTest extends Thread {
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.print(i + " ");
}
}
public static void main(String[] args) {
ThreadTest t = new ThreadTest();
t.start();
}
}
运行结果如下:
1 2 3 4 5 6 7 8 9 10
start() 方法会启动线程,线程自动执行 run() 方法中的代码,就可以看到 for 循环输出的数字了。
在 main() 方法中,使线程执行就需要调用 Thread 类中的 start() 方法,start() 方法调用被覆盖的 run() 方法,如果不调用 start() 方法,线程永远都不会被启动,在主方法没有调用 start() 方法之前,Thread 对象只是一个实例,而不是一个真正的线程。
实现Runnable接口
到目前为止,线程都是通过扩展 Thread 类来创建的,程序员如果需要继承其他类(非 Thread 类),而且还要使当前类实现多线程,那么可以通过 Runnable 接口来实现。例如,一个扩展 JFrame 类的 GUI 程序不可能再继承 Thread 类,因为 Java 语言中不支持多继承,这时该类就需要实现 Runnable 接口使其具有使用线程的功能。实现 Runnable 接口的语法如下:
public class Thread extends Object implements Runnable
有兴趣的读者可以查询 API,从中可以发现,实质上 Thread 类实现了 Runnable 接口,其中的 run() 方法正是对 Runnable 接口中的 run() 方法的具体实现。 |
实现 Runnable 接口的程序会创建一个 Thread 对象,并将 Runnable 对象与 Thread 对象相关联。Thread 类中有以下两个构造方法:
-
public Thread(Runnable target)。
-
public Thread(Runnable target,String name)。
这两个构造方法的参数中都存在 Runnable 实例,使用以上构造方法就可以将 Runnable 实例与 Thread 实例相关联。
使用 Runnable 接口启动新的线程的步骤如下:
-
建立 Runnable 对象。
-
使用参数为 Runnable 对象的构造方法创建 Thread 实例。
-
调用 start() 方法启动线程。
通过 Runnable 接口创建线程时,程序员首先需要编写一个实现 Runnable 接口的类,然后实例化该类的对象,这样就建立了 Runnable 对象,接下来使用相应的构造方法创建 Thread 实例,最后使用该实例调用 Thread 类中的 start() 方法启动线程。图20.2表明了实现 Runnable 接口创建线程的流程。
线程最引人注目的部分应该是与 Swing 相结合创建 GUI 程序,下面演示一个 GUI 程序,该程序实现了图标滚动的功能。

【例20.2】让窗体中的图标动起来(实例位置:资源包\TM\sl\20\2)
在项目中创建 SwingAndThread 类,该类继承 JFrame 类,在 SwingAndThread 类中,使用 Swing 类和线程来移动窗体中的图标。
import java.awt.Container;
import javax.swing.*;
public class SwingAndThread extends JFrame {
int count = 0; // 图标横坐标
public SwingAndThread() {
setBounds(300, 200, 250, 100); // 绝对定位窗体大小与位置
Container container = getContentPane();// 主容器
container.setLayout(null); // 使窗体不使用任何布局管理器
Icon icon = new ImageIcon("src/1.gif"); // 图标对象
JLabel jl = new JLabel(icon);// 显示图标的标签
jl.setBounds(10, 10, 200, 50); // 设置标签的位置与大小
Thread t = new Thread() { // 定义匿名线程对象
public void run() {
while (true) {
jl.setBounds(count, 10, 200, 50); // 将标签的横坐标用变量表示
try {
Thread.sleep(500); // 使线程休眠500毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
count += 4; // 使横坐标每次增加4
if (count >= 200) {
// 当图标到达标签的最右边时,使其回到标签最左边
count = 10;
}
}
}
};
t.start(); // 启动线程
container.add(jl); // 将标签添加到容器中
setVisible(true); // 使窗体可见
// 设置窗体的关闭方式
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new SwingAndThread(); // 实例化一个SwingAndThread对象
}
}
运行本实例,结果如图20.3所示。

在本实例中,为了使图标具有滚动功能,需要在类的构造方法中创建 Thread 实例。在创建该实例的同时需要将 Runnable 对象作为 Thread 类构造方法的参数,然后使用内部类形式实现 run() 方法。在 run() 方法中主要循环图标的横坐标位置,当图标横坐标到达标签的最右方时,再次将图标的横坐标置于图标滚动的初始位置。
启动一个新的线程,不是直接调用 Thread 子类对象的 run() 方法,而是调用 Thread 子类的 start() 方法,Thread 类的 start() 方法产生一个新的线程,该线程运行 Thread 子类的 run() 方法。 |
实现Callable接口
上文已经讲解了 Java 语言中常用的两种实现线程的方式:继承 Thread 类和实现 Runnable 接口。下面将对实现线程的第 3 种方式进行讲解,即实现 Callable 接口。
实现 Runnable 接口和实现 Callable 接口的区别在于以下几点:
-
前者重写的方法是 run() 方法,后者重写的方法是 call() 方法。
-
前者没有返回值,后者有返回值。
-
前者不需要抛出异常,后者需要抛出异常。
通过实现 Callable 接口实现线程的步骤如下:
-
同样创建一个类实现 Callable 接口。
-
使用参数为 Callable 接口的 FutureTask 类的有参构造方法,创建一个 FutureTask 类对象。
-
使用 Thread 类的有参构造方法,创建一个 Thread 类对象。
-
调用 start() 方法启动线程。
-
启动线程后,通过 FutureTask.get() 方法获取到线程的返回值。
下面通过一个实例演示如何通过实现 Callable 接口实现线程。代码如下:
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class CallableTest {
public static void main(String[] args) throws Exception {
FutureTask<String> ft = new FutureTask<>(new CalThread());
Thread thd = new Thread(ft);
thd.start();
System.out.println("已获取线程的返回值!返回值是\n'" + ft.get() + "'");
}
}
class CalThread implements Callable<String> { // 返回值类型是 String
@Override
public String call() throws Exception { // 重写 call() 方法
return "请查收:已通过实现 Callable 接口实现线程!";
}
}
运行结果如下:
已获取线程的返回值!返回值是
“请查收:已通过实现Callable接口实现线程!”
编程训练(答案位置:资源包\TM\sl\20\编程训练)
【训练1】球员入场 足球比赛开赛前,A、B两队的上场球员(每队各11名球员)会依次出现在两个半场处。受球场条件限制,球员通道口每次只能通过一名球员,使用Thread类,模拟球员入场情景。
【训练2】模拟下载进度条 通过实现Runnable接口模拟下载进度条:单击“开始下载”按钮后,“开始下载”按钮失效且进度条从0不断加5,直到加至100。进度条达到100后,失效的“开始下载”按钮变为被启用的“下载完成”按钮,单击“下载完成”按钮后,销毁当前窗体。