Java并发编程:线程创建的三种方式详解

Dylan
Dylan
发布时间:2026-10-16 | 5人浏览过
原创

Java并发编程的核心在于对线程的有效管理。本文将深入探讨Java中创建线程的三种主要方式:继承Thread类、实现Runnable接口以及实现Callable接口。通过分析每种方式的代码结构、优缺点及适用场景,特别是针对获取返回值和多资源共享的需求,为开发者提供清晰的实践指南。

线程与进程的基础概念

理解线程创建之前,需明确进程与线程的关系。进程是程序在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。线程则是CPU调度和分配的基本单位,一个进程中至少包含一个线程,且线程间共享进程的资源。

  • 动态性:进程作为运行中的程序,需要动态占用内存、CPU和网络等资源。
  • 独立性:进程之间相互独立,拥有独立的内存区域。
  • 并发性在单核CPU中,多个进程通过分时轮询切换快速交替执行,从而在宏观上呈现“同时执行”的效果。

方式一:继承 Thread 类

这是最直观的线程创建方式,适合简单的任务处理。其核心步骤包括定义继承Thread的类、重写run()方法,并通过调用start()启动线程。

第一步:定义线程类并继承Thread

创建一个新类MyThread,使其extends Thread。这种方式的优点是编码简单,在run()内部可直接使用this代表当前线程。但缺点是Java不支持多继承,一旦继承Thread,该类便无法再继承其他父类,限制了代码复用。

public class ThreadDemo {
    public static void main(String[] args) {
        // 3.创建一个线程对象
        Thread t = new MyThread();
        // 4.调用线程对象的start()方法启动线程
        t.start();

        for(int i = 0 ; i < 100 ; i++ ){
            System.out.println("main线程输出:"+i);
        }
    }
}

// 1.定义一个线程类继承Thread类。
class MyThread extends Thread{
    // 2.重写run()方法
    @Override
    public void run() {
        // 线程的执行方法。
        for(int i = 0 ; i < 100 ; i++ ){
            System.out.println("子线程输出:"+i);
        }
    }
}

第二步:启动线程

注意必须调用start()而非run()。直接调用run()只是普通方法调用,不会启动新线程。多线程环境下,由于CPU抢占执行,输出结果通常具有随机性。

方式二:实现 Runnable 接口(推荐)

为了解决继承Thread带来的单继承局限性,并实现任务代码与线程对象的解耦,推荐使用Runnable接口。这种方式允许线程任务对象被包装成多个线程,便于资源共享。

第一步:实现Runnable接口并重写run()

创建MyRunnable类实现Runnable接口。此时,MyRunnable只是一个普通的Java对象,不带有线程特性。

第二步:将任务包装为线程并启动

实例化MyRunnable后,将其作为参数传入Thread构造器。这种方式下,同一个Runnable对象可以被多次传给不同的Thread实例,从而实现多个线程共享同一份任务逻辑和资源。

public class ThreadDemo {
    public static void main(String[] args) {
        // 3.创建一个线程任务对象
        Runnable target = new MyRunnable();
        // 4.把线程任务对象包装成线程对象,可指定名称
        Thread t = new Thread(target,"1号线程");
        t.start();

        // 同一个任务对象可以包装成另一个线程
        Thread t2 = new Thread(target);
        t2.start();

        for(int i = 0 ; i < 10 ; i++ ){
            System.out.println(Thread.currentThread().getName()+"==>"+i);
        }
    }
}

// 1.创建一个线程任务类实现Runnable接口。
class MyRunnable implements Runnable{
    // 2.重写run()方法
    @Override
    public void run() {
        for(int i = 0 ; i < 10 ; i++ ){
            System.out.println(Thread.currentThread().getName()+"==>"+i);
        }
    }
}

优势总结:避免了单继承限制;实现了任务与执行的解耦;支持多线程共享同一资源。

方式三:实现 Callable 接口

与前两种方式最大的区别在于,Callable接口的call()方法可以有返回值,并且可以抛出受检异常。这在需要获取异步计算结果的场景中非常关键。

第一步:定义Callable任务并返回结果

创建MyCallable类实现Callable。call()方法用于执行逻辑并返回结果字符串。

第二步:通过FutureTask适配并启动线程

由于Thread构造器只接受Runnable,因此需要使用FutureTask作为中间桥接。FutureTask不仅实现了Runnable,还封装了Callable的回调机制,允许在线程结束后获取结果。

public class ThreadDemo {
    public static void main(String[] args) {
        // 3.创建一个Callable的线程任务对象
        Callable call = new MyCallable();
        
        // 4.将Callable任务包装为FutureTask
        FutureTask task = new FutureTask<>(call);
        
        // 5.把FutureTask对象包装成线程对象
        Thread t = new Thread(task);
        // 6.启动线程对象
        t.start();

        // ... 主线程执行其他逻辑 ...
        for(int i = 1 ; i <= 10 ; i++ ){
            System.out.println(Thread.currentThread().getName()+" => " + i);
        }

        // 获取执行结果,若未完成则阻塞等待
        try {
            String rs = task.get(); 
            System.out.println(rs);
        }  catch (Exception e) {
            e.printStackTrace();
        }
    }
}

// 1.创建Callable任务类
class MyCallable implements Callable{
    // 2.重写call方法
    @Override
    public String call() throws Exception {
        int sum = 0;
        for(int i = 1 ; i <= 10 ; i++ ){
            sum+=i;
        }
        return "执行的结果是:"+sum;
    }
}

关键点:task.get()是一个阻塞方法,如果线程尚未执行完或执行完毕但未取值,该方法会一直等待直到获得结果。这是实现“带返回值的异步调用”的标准做法。

总结与选型建议

在实际开发中,优先推荐使用实现Runnable或Callable接口的方式,以保留类的继承灵活性并实现任务解耦。如果只需要执行无返回值的任务,选择Runnable;如果需要获取线程的执行结果,则选择Callable配合FutureTask。以上就是Java并发编程之线程创建的详细内容,更多关于Java线程高级应用资料请关注本站其它相关文章!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表