Java多线程的3种实现方式(非常详细,附带实例)

365打水账号怎么防止封号 📅 2025-11-20 18:47:39 👤 admin 👁️ 8385 ❤️ 387
Java多线程的3种实现方式(非常详细,附带实例)

Java 语言提供了 3 种实现多线程的方式,继承 Thread 类、实现 Runnable 接口、使用 Callable 接口和 Future 接口。

Java继承Thread类实现多线程

Java 提供了 Thread 类,代表线程,它位于 java.lang 包中,开发人员可以通过继承 Thread 类来创建并启动多线程,具体步骤如下:

从 Thread 类派生出一个子类,并且在子类中重写 run() 方法;

用这个子类创建一个实例对象;

调用对象的 start() 方法启动线程。

启动一个新线程时,需要创建一个 Thread 类的实例,Thread 类的常用构造方法如下表所示。

表:Thread 类的常用构造方法

构造方法

方法描述

public Thread()

创建新的 Thread 对象,自动生成的线程名称为 Thread-n,其中 n 为整数

public Thread(String name)

创建新的 Thread 对象,name 是新线程的名称

public Thread(Runnable target)

创建新的 Thread 对象,其中 target 是 run() 方法被调用时的对象

public Thread(Runnable target, String name)

创建新的 Thread 对象,其中 target 是 run() 方法被调用时的对象,name 是新线程的名称

表中列出了 Thread 类的常用构造方法,创建线程实例时需要使用这些构造方法,线程中真正的功能代码写在这个类的 run() 方法中。当一个类继承 Thread 类之后,要重写父类的 run() 方法。

另外,Thread 类还有一些常用方法,如下表所示:

表 2 Thread 类的常用方法

方法

方法描述

String getName()

返回该线程的名称

Thread.State getState()

返回该线程的状态

boolean isAlive()

判断该线程是不是处于活跃状态

void setName(String name)

更改线程的名字,使其与参数name保持一致

void start()

开始执行线程,Java虚拟机调用该线程里面的run()方法

static void sleep(long millis)

在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器与调度程序精度和准确性的影响

static Thread currentThread()

返回当前正在运行的线程的对象的引用

接下来,通过案例来演示使用继承 Thread 类的方式创建多线程:

public class Demo {

public static void main(String[] args) { // 创建MyThread实例对象

MyThread myThread1 = new MyThread(); // 开启线程

MyThread myThread2 = new MyThread();

myThread1.start();

myThread2.start();

}

}

class MyThread extends Thread { // 重写run()方法

public void run() {

for (int i = 0; i < 10; i++) {

if (i % 2 != 0) {

System.out.println(Thread.currentThread().getName() + ":" + i);

}

}

}

}

程序的运行结果如下:

Thread-0:1

Thread-1:1

Thread-0:3

Thread-1:3

Thread-0:5

Thread-0:7

Thread-0:9

Thread-1:5

Thread-1:7

Thread-1:9

程序中声明了一个 MyThread 类,继承 Thread 类,并且在类中重写了 run() 方法,方法的功能是循环打印小于 10 的奇数,其中 currentThread() 方法是 Thread 类的静态方法,调用该方法返回的是当前正在执行的线程对象的引用。

Demo 类在 main() 方法中创建了两个 MyThread 类的实例对象,分别调用实例对象的 start() 方法启动两个线程,两个线程都运行成功。

注意,如果 start() 方法调用一个已经启动的线程,程序会抛出 IllegalThreadStateException 异常。

Java实现Runnable接口实现多线程

Runnable 是 Java 中用于实现线程的接口,从理论上来讲,任何实现线程功能的类都必须实现该接口。前面讲到的继承 Thread 类的方式创建多线程,实际上就是因为 Thread 类实现了 Runnable 接口,所以它的子类才具有了线程的功能。

但是,Java 只支持单继承,一个类只能有一个父类,当一个类继承 Thread 类之后就不能再继承其他类,因此可以用实现 Runnable 接口的方式创建多线程,这种创建线程的方式更具有灵活性,同时可令用户线程能够具有其他类的一些特性,所以这种方法是经常使用的。

通过实现 Runnable 接口创建并启动多线程的步骤如下:

定义 Runnable 接口实现类,并重写 run() 方法;

创建 Runnable 接口实现类的实例对象,并将该实例对象传递给 Thread 类的一个构造方法,该实例对象提供线程体 run() 方法;

调用实例对象的 start() 方法启动线程。

接下来,通过案例来演示如何通过实现 Runnable 接口的方式创建多线程。

public class Demo {

public static void main(String[] args) { // 创建myThread对象

MyThread myThread = new MyThread(); // 启动线程

new Thread(myThread, "线程1").start();

new Thread(myThread, "线程2").start();

}

}

class MyThread implements Runnable { // 重写run()方法

public void run() {

for (int i = 0; i < 10; i++) {

if (i % 2 != 0) {

System.out.println(Thread.currentThread().getName() + ":" + i);

}

}

}

}

程序的运行结果如下:

线程1:1

线程1:3

线程2:1

线程1:5

线程2:3

线程1:7

线程2:5

线程1:9

线程2:7

线程2:9

程序中,MyThread 类实现了 Runnable 接口并且重写了 run() 方法,方法的功能是循环打印小于 10 的奇数。Demo 类在 main() 方法中以 MyThread 类的实例分别创建并开启两个线程对象,调用 Thread(Runnable target, String name) 构造方法的目的是指定线程的名称“线程1”和“线程2”。

Java通过Callable接口和Future接口实现多线程

前文创建多线程的两种方式都有一个缺陷,在执行完任务之后无法获取线程的执行结果,如果想要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到,这样使用起来就比较麻烦。

于是,JDK 5.0 后 Java 便提供了 Callable 接口来解决这个问题,该接口内有一个 call() 方法,这个方法是线程执行体,有返回值且可以抛出异常。

通过实现 Callable 接口创建并启动多线程的步骤如下:

定义 Callable 接口实现类,指定返回值的类型,并重写 call() 方法。

创建 Callable 实现类的实例。

使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了 Callable 对象的 call() 方法的返回值。

将 FutureTask 类的实例注册进入 Thread 类中并启动线程。

采用 FutureTask 中的 get() 方法获取自定义线程的返回值。

Callable 接口不是 Runnable 接口的子接口,所以不能直接作为 Thread 类构造方法的参数,而且 call() 方法有返回值,是被调用者。JDK 5.0 中提供了 Future 接口,该接口有一个 FutureTask 实现类,该类实现了 Runnable 接口,封装了 Callable 对象的 call() 方法的返回值,所以该类可以作为参数传入 Thread 类中。

接下来,先了解一下 Future 接口的常用方法,如下表所示:

表:Future接口的常用方法

方法

方法描述

boolean cancel(boolean b)

试图取消对该任务的执行

V get()

如有必要,等待计算完成,然后获取其结果

V get(long timeout, TimeUnit unit)

如有必要,最多等待使计算完成所用时间之后,获取其结果(若结果可用)

boolean isCancelled()

如果在任务正常完成前将其取消,则返回 true

boolean isDone()

如果任务已完成,则返回 true

接下来,通过案例来演示如何通过 Callable 接口和 Future 接口创建多线程:

import java.util.concurrent.Callable;

import java.util.concurrent.FutureTask;

public class Demo {

public static void main(String[] args) {

Callable callable = new MyThread(); // 创建Callable对象

// 使用FutureTask来包装Callable对象

FutureTask futureTask = new FutureTask(callable);

for (int i = 0; i < 15; i++) {

System.out.println(Thread.currentThread().getName() + ":" + i);

if (i == 1) {

// FutureTask对象作为Thread对象的参数创建新的线程

Thread thread = new Thread(futureTask);

thread.start(); // 启动线程

}

}

System.out.println("主线程循环执行完毕");

try {

// 取得新创建线程中的call()方法返回值

String result = futureTask.get();

System.out.println("result = " + result);

} catch (Exception e) {

e.printStackTrace();

}

}

}

class MyThread implements Callable {

public String call() {

for (int i = 10; i > 0; i--) {

System.out.println(Thread.currentThread().getName() + "倒计时:" + i);

}

return "线程执行完毕!!!";

}

}

程序的第 1 次运行结果如下:

main:0

main:1

main:2

main:3

main:4

main:5

main:6

main:7

main:8

main:9

main:10

main:11

main:12

main:13

main:14

主线程循环执行完毕

Thread-0倒计时:10

Thread-0倒计时:9

Thread-0倒计时:8

Thread-0倒计时:7

Thread-0倒计时:6

Thread-0倒计时:5

Thread-0倒计时:4

Thread-0倒计时:3

Thread-0倒计时:2

Thread-0倒计时:1

result = 线程执行完毕!!!

程序的第 2 次运行结果如下:

main:0

main:1

main:2

main:3

main:4

main:5

main:6

main:7

main:8

main:9

Thread-0倒计时:10

main:10

Thread-0倒计时:9

main:11

Thread-0倒计时:8

main:12

Thread-0倒计时:7

main:13

main:14

主线程循环执行完毕

Thread-0倒计时:6

Thread-0倒计时:5

Thread-0倒计时:4

Thread-0倒计时:3

Thread-0倒计时:2

Thread-0倒计时:1

result = 线程执行完毕!!!

程序中 MyThread 类实现了 Callable 接口,指定了返回值的类型并且重写了 call() 方法。该方法主要是用于打印倒计时的时间。main() 方法中执行 15 次循环,并且在循环的过程中启动子线程并获取子线程的返回值。

反复执行实例程序,会发现有一个规律:“result = 线程执行完毕!!!”一直都是在最后输出,而“主线程循环执行完毕”输出的位置则不固定,有时候会在子线程循环前,有时候会在子线程循环后,有时候也会在子线程循环中。

之所以会出现这种现象,是因为通过 get() 方法获取子线程的返回值时,子线程的方法没有执行完毕,所以 get() 方法就会阻塞,当子线程中的 call() 方法执行完毕,get() 方法才能取到返回值。

3种实现多线程方式的对比

前面讲解了创建多线程的 3 种方式,这 3 种方式各有优缺点,具体如下表所示。

表:3 种实现多线程方式的对比

实现方式

优点

缺点

继承 Thread 类

程序代码简单

使用 run() 方法可以直接调用线程的其他方法

只能继承 Thread 类

不能实现资源共享

实现 Runnable 接口

符合面向对象的设计思想

便于继承其他的类

能实现资源共享

编程比较复杂

使用 Callable 接口和 Future 接口

便于继承其他的类

有返回值,可以抛异常

编程比较复杂

上表列出了 3 种创建多线程方式的优点和缺点,想要代码简洁就采用第 1 种方式,想要实现资源共享就采用第 2 种方式,想要有返回值并且能抛异常就采用第 3 种方式。

相关养生推荐

体重身高标准对照表及计算公式(2024身高体重标准表最新版)
365打水账号怎么防止封号

体重身高标准对照表及计算公式(2024身高体重标准表最新版)

📅 08-08 👁️ 1835
九天揽月是什么生肖
beat365官方入口素描网

九天揽月是什么生肖

📅 10-30 👁️ 3039
美宝莲 橡皮擦遮瑕笔
国内在365投注

美宝莲 橡皮擦遮瑕笔

📅 07-12 👁️ 6662
粉丝答疑:电脑中了病毒如何自救?
beat365官方入口素描网

粉丝答疑:电脑中了病毒如何自救?

📅 07-10 👁️ 3086
《剑与家园》公会系统详解(加入公会的方法与技巧)
365打水账号怎么防止封号

《剑与家园》公会系统详解(加入公会的方法与技巧)

📅 08-17 👁️ 8292
三星S8国米怎么样?(国产手机与三星S8的比较及性能评测)
365打水账号怎么防止封号

三星S8国米怎么样?(国产手机与三星S8的比较及性能评测)

📅 10-02 👁️ 9241