Java多线程的3种实现方法(非常详细,附带实例)
在 Java 中有三种多线程实现方式:
通过 Thread 类或它的子类可以创建线程实例对象,并启动一个新的线程,Thread 类的构造方法如下:
当上述构造方法缺少某个参数时,就变成了其他的构造方法,Thread 类共有 8 个重载的构造方法,这里不再赘述。
Thread 类中常用的方法如下表所示。
编写 Thread 类的派生类,主要覆盖 Thread 类的 run() 方法,在这个方法的方法体中加入线程所要执行的代码即可。因此经常把 run() 方法称为线程的执行体。
run() 方法可以调用其他方法,使用其他类或声明变量,就像主线程 main() 方法一样。线程的 run() 方法运行结束,线程也将终止。
通过继承 Thread 类创建线程的步骤如下:
创建一个线程对象后,仅仅是在内存中出现了一个线程类的实例对象,线程并不会自动开始运行,必须调用线程对象的 start() 方法来启动线程。
启动线程主要完成两方面的任务:
【实例】通过继承 Thread 类来实现一个线程类,在主线程中创建并启动两个线程,这两个线程会在给定的时间间隔显示它们的状态信息。
可以通过实现 Runnable 接口的方式创建线程。Runnable 接口只有一个 run() 方法,我们声明的类需要实现这一方法。这里的run()方法同样也可以调用其他方法。
通过实现 Runnable 接口创建线程的步骤如下:
【实例】通过实现 Runnable 接口来实现一个线程类,在主线程中创建并启动线程,线程执行时,会在给定的时间间隔不断显示系统当前时间。
从 Java 5 开始提供 Callable 和 Future 接口,通过它们可以在任务执行结束后得到任务的执行结果。
通过 Callable 和 Future 创建线程的步骤如下:
【实例】通过实现 Callable 和 Future 接口来实现一个线程类,在主线程中创建并启动子线程对象,子线程执行完毕,返回循环变量的值。
- 继承 Thread 类;
- 实现 Runnable 接口;
- 通过 Callable 和 Future 创建线程。
继承Thread类
Thread 类位于 java.lang 包中,Thread 的每个实例对象就是一个线程。通过 Thread 类或它的子类可以创建线程实例对象,并启动一个新的线程,Thread 类的构造方法如下:
public Thread(ThreadGroup group,Runnable target,String name,long stackSize);其中,group 指明该线程所属的线程组,target 为实际执行线程体的目标对象,name 为线程名,stackSize 为线程指定的堆栈大小。
当上述构造方法缺少某个参数时,就变成了其他的构造方法,Thread 类共有 8 个重载的构造方法,这里不再赘述。
Thread 类中常用的方法如下表所示。
方法 | 方法说明 |
---|---|
void run() | 线程运行时所执行的代码都在这个方法中,是 Runnable 接口声明的唯一方法 |
void start() | 使线程开始执行;Java 虚拟机调用该线程的 run 方法 |
static int activeCount() | 返回当前线程的线程组中活动线程的数目 |
static Thread currentThread() | 返回对当前正在执行的线程对象的引用 |
static int enumerate(Thread[] t) | 将当前线程组中的每一个活动线程复制到指定的数组中 |
String getName() | 返回线程的名称 |
int getPriority() | 返回线程的优先级 |
Thread.State getState() | 返回线程的状态 |
Thread Group getThreadGroup() | 返回线程所属的线程组 |
final boolean isAlive() | 测试线程是否处于活动状态 |
void setDaemon(boolean on) | 将线程标记为守护线程或用户线程 |
void setName(String name) | 改变线程名称,使其与参数name相同 |
void interrupt() | 中断线程 |
void join() | 等待该线程终止,有多个重载方法 |
static void yield() | 暂停当前正在执行的线程对象,并执行其他线程 |
编写 Thread 类的派生类,主要覆盖 Thread 类的 run() 方法,在这个方法的方法体中加入线程所要执行的代码即可。因此经常把 run() 方法称为线程的执行体。
run() 方法可以调用其他方法,使用其他类或声明变量,就像主线程 main() 方法一样。线程的 run() 方法运行结束,线程也将终止。
通过继承 Thread 类创建线程的步骤如下:
- 步骤01:定义 Thread 类的子类,并重写 run() 方法,实现线程的功能;
- 步骤02:创建 Thread 子类的实例,即创建线程对象;
- 步骤03:调用线程对象的 start() 方法启动该线程。
创建一个线程对象后,仅仅是在内存中出现了一个线程类的实例对象,线程并不会自动开始运行,必须调用线程对象的 start() 方法来启动线程。
启动线程主要完成两方面的任务:
- 一方面为线程分配必要的资源,使线程处于可运行状态;
- 另一方面调用线程的 run() 方法来运行线程。
【实例】通过继承 Thread 类来实现一个线程类,在主线程中创建并启动两个线程,这两个线程会在给定的时间间隔显示它们的状态信息。
class ThreadDemo extends Thread { private Thread t; private String threadName; ThreadDemo(String name) { threadName = name; System.out.println("创建线程:" + threadName); } public void run() { System.out.println("运行线程:" + threadName); try { for(int i = 0; i < 2; i++) { System.out.println("当前线程:" + threadName); // 让线程睡一会 Thread.sleep(1000); } }catch (InterruptedException e) { System.out.println("线程:" + threadName + "中断。"); } System.out.println("线程:" + threadName + "结束。"); } public void start() { System.out.println("线程启动:" + threadName); if (t == null) { t = new Thread(this, threadName); t.start(); } } } public class TestThread { public static void main(String args[]) { ThreadDemo T1 = new ThreadDemo("Thread1"); T1.start(); ThreadDemo T2 = new ThreadDemo("Thread2"); T2.start(); } }在本例中,每间隔 1 秒在屏幕上显示当前线程信息,程序运行结果为:
创建线程:Thread1
线程启动:Thread1
创建线程:Thread2
线程启动:Thread2
运行线程:Thread1
当前线程:Thread1
运行线程:Thread2
当前线程:Thread2
当前线程:Thread1
当前线程:Thread2
线程:Thread1 结束。
线程:Thread2 结束。
实现Runnable接口
通过继承 Thread 类来创建线程类有一个缺点,那就如果要创建的线程类已经继承了一个其他类,则无法再继承 Thread 类。可以通过实现 Runnable 接口的方式创建线程。Runnable 接口只有一个 run() 方法,我们声明的类需要实现这一方法。这里的run()方法同样也可以调用其他方法。
通过实现 Runnable 接口创建线程的步骤如下:
- 定义 Runnable 接口的实现类,并实现该接口的 run() 方法;
- 创建 Runnable 实现类的实例,并以此实例作为 Thread 类的 target 参数来创建 Thread 线程对象,该对象才是真正的线程对象;
【实例】通过实现 Runnable 接口来实现一个线程类,在主线程中创建并启动线程,线程执行时,会在给定的时间间隔不断显示系统当前时间。
import java.util.*; class TimePrinter implements Runnable { public boolean stop = false; // 线程是否停止 int pauseTime; // 时钟跳变时间间隔 String name; // 显示时间的标签 public TimePrinter(int x, String n) { // 构造方法,初始化成员变量 pauseTime = x; name = n; } public void run() { while (!stop) { try { // 在控制台中显示系统的当前日期和时间 System.out.println(name + ":" + new Date(System.currentTimeMillis())); Thread.sleep(pauseTime); // 线程睡眠 pauseTime 毫秒 } catch (Exception e) { e.printStackTrace(); // 输出异常信息 } } } } public class TestRunnable { public static void main(String args[]) { // 实例化一个Runnable 对象 TimePrinter tp = new TimePrinter(1000, "当前日期时间"); Thread t = new Thread(tp); // 实例化一个线程对象 t.start(); // 启动线程 System.out.println("按回车键终止! "); try { System.in.read(); // 从输入缓冲区中读取数据,按回车键返回 } catch (Exception e) { e.printStackTrace(); // 输出异常信息 } tp.stop = true; // 置子线程的终止标志为true } }在本例中,每间隔 1 秒在屏幕上显示当前时间。这是由主线程创建的一个新线程来完成的。程序运行结果为:
按回车键终止!
当前日期时间:Tue Jun 03 09:55:14 UTC 2025
通过Callable和Future创建线程
前面介绍的两种方式都需要调用 Thread 类中的 start() 方法启动线程并调用 run() 方法。由于线程的 run() 方法没有返回值,如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来实现,这样做比较麻烦。从 Java 5 开始提供 Callable 和 Future 接口,通过它们可以在任务执行结束后得到任务的执行结果。
通过 Callable 和 Future 创建线程的步骤如下:
- 步骤 01:创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值;
- 步骤 02:创建 Callable 实现类的实例,使用 FutureTask 类包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值;
- 步骤 03:使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程;
- 步骤 04:调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。
【实例】通过实现 Callable 和 Future 接口来实现一个线程类,在主线程中创建并启动子线程对象,子线程执行完毕,返回循环变量的值。
import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; import java.util.concurrent.ExecutionException; public class CallableThreadTest implements Callable<Integer> { public static void main(String[] args) { CallableThreadTest ctt = new CallableThreadTest(); FutureTask<Integer> ft = new FutureTask<Integer>(ctt); for(int i = 0; i < 5;i++) { System.out.println(Thread.currentThread().getName()+"的循环变量i的值"+i); if(i==2){ new Thread(ft,"有返回值的线程").start(); } } try{ // 通过 get() 方法获取线程的 run() 方法的返回值 System.out.println("子线程的返回值:"+ft.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } public Integer call() throws Exception { int i = 0; for(;i<3;i++){ System.out.println(Thread.currentThread().getName()+""+i); } return i; } }在本例中,子线程执行结束后,循环变量的值为 3。程序运行结果为:
main 的循环变量 i 的值 0
main 的循环变量 i 的值 1
main 的循环变量 i 的值 2
main 的循环变量 i 的值 3
main 的循环变量 i 的值 4
有返回值的线程 0
有返回值的线程 1
有返回值的线程 2
子线程的返回值:3