首页 > 编程笔记 > Java笔记 阅读:36

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

在 Java 中有三种多线程实现方式:
  1. 继承 Thread 类;
  2. 实现 Runnable 接口;
  3. 通过 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 类中常用的方法如下表所示。

表: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 类创建线程的步骤如下:
创建一个线程对象后,仅仅是在内存中出现了一个线程类的实例对象,线程并不会自动开始运行,必须调用线程对象的 start() 方法来启动线程。

启动线程主要完成两方面的任务:
【实例】通过继承 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 接口来实现一个线程类,在主线程中创建并启动线程,线程执行时,会在给定的时间间隔不断显示系统当前时间。
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

通过实现 Runnable 接口实现的线程类创建的对象不能直接运行,需要把该对象作为参数传递给 Thread 类的构造方法,在此基础上创建 Thread 对象,然后通过 Thread 对象调用 start() 方法开启线程。

通过Callable和Future创建线程

前面介绍的两种方式都需要调用 Thread 类中的 start() 方法启动线程并调用 run() 方法。由于线程的 run() 方法没有返回值,如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来实现,这样做比较麻烦。

从 Java 5 开始提供 Callable 和 Future 接口,通过它们可以在任务执行结束后得到任务的执行结果。

通过 Callable 和 Future 创建线程的步骤如下:
【实例】通过实现 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

相关文章