0

0

java怎样创建和启动多线程程序 java多线程编程的基础操作方法

蓮花仙者

蓮花仙者

发布时间:2025-08-08 18:10:02

|

742人浏览过

|

来源于php中文网

原创

java中创建和启动多线程程序的核心方法有两种:1. 实现runnable接口,将任务逻辑与线程解耦,便于任务复用和线程池管理;2. 继承thread类,直接定义线程行为,但受限于java单继承机制。应优先选择实现runnable接口,因其更符合单一职责原则且灵活性更高。启动线程必须调用start()方法,它会由jvm创建新线程并异步执行run()中的任务;若直接调用run(),则仅作为普通方法在当前线程同步执行,无法实现并发。线程生命周期包括五种状态:new(新建)、runnable(可运行)、blocked(阻塞)、waiting(无限等待)、timed_waiting(限时等待)和terminated(终止),理解这些状态有助于分析和调试多线程程序的执行行为。

java怎样创建和启动多线程程序 java多线程编程的基础操作方法

Java中创建和启动多线程程序,核心在于定义好线程要执行的任务,然后通过

Thread
类来调度和启动这个任务。这通常有两种基本方式:要么让你的任务类实现
Runnable
接口,要么直接继承
Thread
类。无论哪种,最终都是通过调用
Thread
实例的
start()
方法来真正启动一个新线程。

解决方案

创建和启动多线程程序,我们通常会选择以下两种路径:

1. 实现

Runnable
接口

立即学习Java免费学习笔记(深入)”;

这是更推荐的方式,因为它将任务(

Runnable
)与线程(
Thread
)本身解耦。一个类可以实现多个接口,但只能继承一个类,所以用
Runnable
能更好地规避Java的单继承限制。

  • 定义任务: 创建一个类实现

    Runnable
    接口,并重写其
    run()
    方法。
    run()
    方法里就是你希望新线程执行的代码逻辑。

    class MyRunnableTask implements Runnable {
        private String taskName;
    
        public MyRunnableTask(String name) {
            this.taskName = name;
        }
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " 正在执行任务: " + taskName);
            try {
                // 模拟任务执行耗时
                Thread.sleep(100 + (long)(Math.random() * 500));
            } catch (InterruptedException e) {
                System.out.println(taskName + " 被中断了!");
                Thread.currentThread().interrupt(); // 重新设置中断状态
            }
            System.out.println(Thread.currentThread().getName() + " 完成了任务: " + taskName);
        }
    }
  • 创建并启动线程: 实例化

    MyRunnableTask
    ,然后将其作为参数传递给
    Thread
    类的构造器,最后调用
    Thread
    对象的
    start()
    方法。

    public class ThreadWithRunnableDemo {
        public static void main(String[] args) {
            System.out.println("主线程开始...");
    
            // 创建Runnable任务实例
            Runnable task1 = new MyRunnableTask("下载文件A");
            Runnable task2 = new MyRunnableTask("处理数据B");
            Runnable task3 = new MyRunnableTask("发送邮件C");
    
            // 创建Thread实例并传入Runnable任务
            Thread thread1 = new Thread(task1, "工作线程-1");
            Thread thread2 = new Thread(task2, "工作线程-2");
            Thread thread3 = new Thread(task3, "工作线程-3");
    
            // 启动线程
            thread1.start();
            thread2.start();
            thread3.start();
    
            System.out.println("主线程继续执行,不再等待子线程...");
            // 主线程可以继续做自己的事情
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("主线程结束。");
        }
    }

2. 继承

Thread

这种方式更直接,但由于Java的单继承特性,你的任务类就不能再继承其他类了。

  • 定义任务: 创建一个类继承

    Thread
    类,并重写其
    run()
    方法。

    class MyThread extends Thread {
        private String taskName;
    
        public MyThread(String name) {
            super(name); // 调用父类构造器设置线程名
            this.taskName = name;
        }
    
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " 正在执行任务: " + taskName);
            try {
                // 模拟任务执行耗时
                Thread.sleep(50 + (long)(Math.random() * 300));
            } catch (InterruptedException e) {
                System.out.println(taskName + " 被中断了!");
                Thread.currentThread().interrupt();
            }
            System.out.println(Thread.currentThread().getName() + " 完成了任务: " + taskName);
        }
    }
  • 创建并启动线程: 直接实例化

    MyThread
    类,然后调用其
    start()
    方法。

    public class ThreadExtendsDemo {
        public static void main(String[] args) {
            System.out.println("主线程开始...");
    
            // 创建MyThread实例
            MyThread threadA = new MyThread("独立线程-A");
            MyThread threadB = new MyThread("独立线程-B");
    
            // 启动线程
            threadA.start();
            threadB.start();
    
            System.out.println("主线程继续执行...");
            try {
                Thread.sleep(800);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("主线程结束。");
        }
    }

Java多线程中,Runnable和Thread有什么区别,我该如何选择?

这确实是个老生常谈的问题,但对于初学者来说,弄清楚它至关重要。简单来说,

Runnable
是一个接口,它定义了“要执行什么任务”,而
Thread
是一个类,它定义了“如何执行这个任务”。

Runnable
的本质,是把任务的逻辑和线程的控制分开了。当你实现
Runnable
时,你只是告诉Java虚拟机:“嘿,这是我想让某个线程去跑的代码。”至于哪个线程来跑,怎么调度,那都是
Thread
类的事情。这种分离带来的好处是显而易见的:你的任务类可以专注于业务逻辑,不用关心线程的生命周期管理。更重要的是,Java是单继承的语言,如果你已经继承了某个业务类,就不能再继承
Thread
了。而实现接口则没有这个限制,你可以实现多个接口,包括
Runnable
。这让
Runnable
在实际项目中更具灵活性和复用性,特别是在线程池的场景下,你通常提交的就是
Runnable
任务。

相比之下,继承

Thread
类显得更直接,但也有其局限性。当你继承
Thread
时,你的类“就是”一个线程。这意味着你的业务逻辑和线程行为紧密耦合在一起。如果你想让多个线程执行同一个任务,你可能需要创建多个
Thread
子类的实例,每个实例都包含一份任务逻辑。这在资源共享上可能会带来一些不便。

所以,我的建议是:

歌者PPT
歌者PPT

歌者PPT,AI 写 PPT 永久免费

下载
  • 优先使用
    Runnable
    绝大多数情况下,你只需要定义一个可执行的任务,而不是去定义一个全新的线程类型。它更符合面向对象设计原则中的“单一职责原则”,任务就是任务,线程就是线程。
  • 只有当你需要扩展
    Thread
    类的行为时,才考虑继承
    Thread
    比如,你需要自定义线程的一些特定行为,或者为线程添加一些特有的属性和方法,而不仅仅是执行一个任务。但这样的场景相对较少。

从我的个人经验来看,当你开始接触更高级的并发工具,比如

ExecutorService
(线程池),你会发现它们都是围绕
Runnable
(或
Callable
)设计的。这进一步印证了
Runnable
作为任务定义者的核心地位。

启动线程时,为什么是调用start()而不是直接调用run()?

这是一个非常常见的误区,也是初学者经常会犯的错误。直观上,我们看到

run()
方法里是线程要执行的代码,就觉得直接调用它就行了。但实际上,
start()
run()
的调用效果是天壤之别。

当你调用一个

Thread
对象的
start()
方法时,JVM会做一系列底层操作:

  1. 分配系统资源: JVM会向操作系统申请创建一个新的线程。操作系统会为这个新线程分配独立的栈空间、程序计数器等资源。
  2. 线程注册: 这个新创建的线程会被注册到JVM的线程调度器中,等待被CPU调度执行。
  3. 异步执行:
    start()
    方法会立即返回,而
    run()
    方法里的代码则会在这个新创建的线程中异步、并发地执行。这意味着调用
    start()
    的主线程(或者说,当前线程)不会被阻塞,它可以继续执行自己的代码。

而如果你直接调用

run()
方法呢?

  1. 普通方法调用:
    run()
    方法就只是一个普通的Java方法调用。
  2. 同步执行: 它的代码会在当前线程中同步执行。也就是说,哪个线程调用了
    run()
    run()
    方法里的代码就在哪个线程里执行。它不会创建任何新的线程,也不会有任何并发的效果。调用
    run()
    的线程会一直等到
    run()
    方法执行完毕,才继续执行它后面的代码。

想象一下,你有一个快递员(线程),他需要去送包裹(任务)。

start()
就像是快递公司给你安排了一个新的快递员,他会独立地去送包裹,你可以在家里继续做自己的事情。而直接调用
run()
,就相当于你自己拿起包裹,亲自去送了,你得等到送完才能回来做别的事。

所以,要真正实现多线程和并发,

start()
是唯一的正确入口。它才是启动一个全新执行流的关键。

Java多线程程序运行中,有哪些常见的线程状态和生命周期?

理解线程的生命周期和状态对于调试和优化多线程程序至关重要。一个线程从诞生到消亡,会经历不同的状态。Java的

Thread.State
枚举定义了这些状态,它们分别是:

  1. NEW (新建):

    • 当你使用
      new Thread()
      创建了一个线程对象,但还没有调用它的
      start()
      方法时,线程就处于这个状态。
    • 它只是一个普通的Java对象,还没有被操作系统识别为线程。
  2. RUNNABLE (可运行/运行中):

    • 当你调用了线程的
      start()
      方法后,线程就进入了
      Runnable
      状态。
    • 这个状态表示线程可能正在运行(获得了CPU时间片),或者它已经准备好运行(正在等待CPU调度)。
    • Java的
      Runnable
      状态包含了操作系统层面的“运行中”和“就绪”两种状态。
  3. BLOCKED (阻塞):

    • 当一个线程试图获取一个对象的内部锁(也称为监视器锁,
      synchronized
      关键字)但该锁已经被其他线程持有,它就会进入
      BLOCKED
      状态。
    • 线程会一直等待,直到它能够获取到所需的锁。
  4. WAITING (等待):

    • 线程进入无限期等待状态,直到另一个线程执行了特定的操作来唤醒它。
    • 常见进入
      WAITING
      状态的方法有:
      • Object.wait()
        :当一个线程在某个对象上调用
        wait()
        方法时,它会释放该对象的锁并进入
        WAITING
        状态。
      • Thread.join()
        :当一个线程调用另一个线程的
        join()
        方法时,它会等待被
        join
        的线程执行完毕。
      • LockSupport.park()
        :JUC(
        java.util.concurrent
        )包中的低级别同步原语。
  5. TIMED_WAITING (有时限等待):

    • 线程在指定的时间内等待另一个线程执行特定操作,或者等待指定的时间过去。
    • WAITING
      类似,但有时间限制。
    • 常见进入
      TIMED_WAITING
      状态的方法有:
      • Thread.sleep(long millis)
        :线程休眠指定时间。
      • Object.wait(long timeout)
        :在指定时间内等待对象锁。
      • Thread.join(long millis)
        :在指定时间内等待被
        join
        的线程。
      • LockSupport.parkNanos(Object blocker, long nanos)
        /
        LockSupport.parkUntil(long deadline)
  6. TERMINATED (终止):

    • 线程的
      run()
      方法执行完毕,或者因异常而退出,线程就进入
      TERMINATED
      状态。
    • 一旦线程进入
      TERMINATED
      状态,它就不能再被重新启动了。如果你尝试再次调用
      start()
      ,会抛出
      IllegalThreadStateException

这些状态构成了线程的完整生命周期。理解它们,能帮助我们更好地分析线程的运行情况,比如为什么某个线程“卡住”了(可能是

BLOCKED
WAITING
),或者为什么没有并发效果(可能
start()
没被正确调用,
run()
直接执行了)。在实际开发中,使用JMX工具或者JDK自带的
jstack
命令,可以查看JVM中所有线程的当前状态,这对于诊断并发问题非常有帮助。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

58

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

63

2025.11.27

go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

58

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

63

2025.11.27

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1948

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

658

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2401

2025.12.29

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

47

2026.01.19

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 4.4万人学习

C# 教程
C# 教程

共94课时 | 11.3万人学习

Java 教程
Java 教程

共578课时 | 81.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号