如何在程序中创建出多条线程

news2024/9/25 17:17:47

多线程是编程中的一个重要概念,它允许程序同时执行多个任务,每个任务可以看作是一个线程。在Java中,多线程尤为常见且强大,它通过允许程序在并发环境下运行,提高了程序的执行效率和响应速度。以下是对Java多线程的详细讲解:

基本概念

  1. 线程(Thread):线程是进程中的实体,是CPU调度和分派的基本单位,它是比进程更小的独立运行的单位。线程一般不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器、一组寄存器和栈),但它可与同属一个进程的其他的线程共享进程所拥有的全部资源。
  2. 进程(Process):进程是程序的一次动态执行过程,是程序代码、数据和相关资源的集合。一个进程可以拥有多个线程,这些线程共享进程的地址空间和系统资源。

Thread 类

在Java中,Thread 类是处理多线程的核心类。每个线程都是通过 Thread 类的一个实例来表示的。Java 允许你继承 Thread 类来创建新的线程类,或者实现 Runnable 接口。下面我将详细解释 Thread 类中的几个关键方法。

Thread类的常用方法

  1. public void run()

    线程的任务方法。当线程启动时,会自动调用此方法。在继承Thread类创建线程时,通常需要重写此方法以定义线程的具体任务。
  2. public void start()

    启动线程。调用此方法后,线程会进入就绪状态,等待CPU调度执行。注意,直接调用run()方法并不会启动新线程,而是像普通方法一样在当前线程中执行。
  3. public String getName()

    获取当前线程的名称。默认情况下,线程的名称是"Thread-索引",其中索引是一个递增的整数。
  4. public void setName(String name)

    为线程设置名称。通过此方法可以自定义线程的名称,便于在调试和日志记录中识别不同的线程。
  5. public static Thread currentThread()

    获取当前执行的线程对象。此方法允许在代码中获取当前正在执行的线程实例,进而可以调用该线程的方法或属性。
  6. public static void sleep(long time)

    让当前执行的线程休眠指定的毫秒数后,再继续执行。这是一个静态方法,用于暂停当前线程的执行,让出CPU资源给其他线程。
  7. public final void join()

    让调用当前这个方法的线程先执行完。这个方法的作用是等待调用它的线程(即当前线程)终止。在join()方法返回之前,其他线程(即调用join()方法的线程)无法继续执行。

Thread类的常见构造器

  1. public Thread(String name)

    可以为当前线程指定名称。通过构造器中的name参数,可以为线程设置一个易于识别的名称。
  2. public Thread(Runnable target)

    封装Runnable对象成为线程对象。这种方式是实现多线程的另一种途径,即实现Runnable接口。通过这种方式,可以将线程的任务与线程本身分离,使得代码更加灵活。
  3. public Thread(Runnable target, String name)

    封装Runnable对象成为线程对象,并指定线程名称。这个构造器结合了上述两种构造器的功能,既可以将线程的任务与线程本身分离,又可以自定义线程的名称。

创建方式一

通过继承Thread类来创建线程

步骤 1: 定义子类继承Thread

首先,你需要定义一个子类来继承Java的java.lang.Thread类。在这个子类中,你需要重写run方法。run方法是线程启动时执行的代码块。

步骤 2: 创建MyThread类的对象

接着,你需要创建MyThread类的一个或多个对象。这些对象就是线程实例。

步骤 3: 调用线程对象的start方法启动线程

最后,你需要调用线程对象的start方法来启动线程。调用start方法会启动一个新的线程,并让这个线程执行其run方法中的代码。注意,不要直接调用run方法,这样会导致代码在调用它的线程(通常是主线程)中顺序执行,而不会创建新的线程。

为了举一个更明显的例子来说明通过继承Thread类来创建线程,我们可以考虑一个简单的场景:有两个线程,一个负责打印奇数,另一个负责打印偶数。我们将分别创建两个类(OddThreadEvenThread)来继承Thread类,并在它们的run方法中实现打印奇数或偶数的逻辑。

// OddThread 类,继承自 Thread 类,用于打印奇数  
class OddThread extends Thread {  
    private int start;  
    private int end;  
  
    public OddThread(int start, int end) {  
        this.start = start;  
        this.end = end;  
    }  
  
    @Override  
    public void run() {  
        for (int i = start; i <= end; i += 2) {  
            System.out.println(Thread.currentThread().getName() + " 打印奇数: " + i);  
            try {  
                // 为了更明显地看到线程切换,可以添加一些延迟  
                Thread.sleep(100);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
}  
  
// EvenThread 类,继承自 Thread 类,用于打印偶数  
class EvenThread extends Thread {  
    private int start;  
    private int end;  
  
    public EvenThread(int start, int end) {  
        this.start = start;  
        if (end % 2 == 0) {  
            this.end = end;  
        } else {  
            this.end = end - 1; // 确保结束值是偶数  
        }  
    }  
  
    @Override  
    public void run() {  
        for (int i = start; i <= end; i += 2) {  
            System.out.println(Thread.currentThread().getName() + " 打印偶数: " + i);  
            try {  
                // 为了更明显地看到线程切换,可以添加一些延迟  
                Thread.sleep(100);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
}  
  
// 主类  
public class ThreadExample {  
    public static void main(String[] args) {  
        // 创建并启动打印奇数的线程  
        OddThread oddThread = new OddThread(1, 10);  
        oddThread.start();  
  
        // 创建并启动打印偶数的线程  
        EvenThread evenThread = new EvenThread(2, 10);  
        evenThread.start();  
  
        // 注意:主线程会继续执行,不会等待oddThread和evenThread执行完成  
    }  
}

由于线程的执行是并发的,所以输出结果的顺序可能会有所不同,这取决于JVM的线程调度策略以及操作系统的线程管理机制。

优缺点 

优点:编码简单

缺点:线程类已经继承Thread,无法继承其他类,不利于功能的扩展。

注意

创建方式二

通过实现Runnable接口方式 

1. 为什么使用Runnable接口?

在Java中,创建线程主要有两种方式:继承Thread类和实现Runnable接口。虽然继承Thread类是一种直观的方式,但它限制了类的继承体系(因为Java不支持多重继承)。相比之下,实现Runnable接口更为灵活,因为它允许你的类继承自其他类,并且仍然可以拥有多线程的能力。

2. Runnable接口简介

Runnable是一个函数式接口(从Java 8开始),它只定义了一个方法:run()。当你创建了一个实现了Runnable接口的类的实例后,你可以将这个实例作为参数传递给Thread类的构造函数,从而创建一个新的线程。

线程创建的基本步骤(使用Runnable接口)

①定义Runnable实现类
首先,你需要定义一个类来实现Runnable接口。实现接口意味着你必须提供run方法的实现。这个run方法将包含线程执行时所需的所有代码。

class MyTask implements Runnable {  
    @Override  
    public void run() {  
        // 在这里编写线程的任务代码  
        System.out.println(Thread.currentThread().getName() + " is running.");  
        // 假设这里有一些耗时的操作或逻辑处理  
    }  
}

②创建Runnable实现类的实例
一旦你定义了Runnable实现类,就可以创建这个类的实例了。这个实例将作为线程执行的任务。 

Runnable myTask = new MyTask();

③将Runnable实例传递给Thread构造函数
接下来,你需要将Runnable实例作为参数传递给Thread类的构造函数。这个构造函数会创建一个新的Thread对象,这个对象封装了Runnable实现类的实例。

Thread myThread = new Thread(myTask, "MyCustomThread");

注意,这里的第二个参数是可选的,用于指定线程的名称。如果省略,线程将使用默认名称。

启动线程
最后,你需要调用线程的start方法来启动线程。调用start方法会导致JVM调用Runnable实现类的run方法,在新的线程中执行。

myThread.start();

重要的是要区分start方法和run方法。start方法用于启动线程,而run方法则定义了线程执行的任务。如果你直接调用run方法(如myTask.run()),那么run方法中的代码将在当前线程中同步执行,而不是在新的线程中。

完整示例

将上述步骤组合起来,我们得到以下完整的示例:

class MyTask implements Runnable {  
    @Override  
    public void run() {  
        for (int i = 0; i < 5; i++) {  
            System.out.println(Thread.currentThread().getName() + " is running, iteration " + i);  
            try {  
                // 模拟耗时操作  
                Thread.sleep(1000);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
}  
  
public class Main {  
    public static void main(String[] args) {  
        Runnable myTask = new MyTask();  
          
        // 创建并启动第一个线程  
        Thread thread1 = new Thread(myTask, "Thread-1");  
        thread1.start();  
          
        // 创建并启动第二个线程(注意:这里可以创建任意数量的线程)  
        Thread thread2 = new Thread(myTask, "Thread-2");  
        thread2.start();  
          
        // main线程继续执行其他任务或结束  
        System.out.println("Main thread is ending.");  
    }  
}

 在这个示例中,我们创建了两个线程(thread1thread2),它们都执行相同的任务(由MyTask类的run方法定义)。这两个线程将并发执行,各自打印出自己的迭代次数,并模拟了耗时操作(通过Thread.sleep)。同时,main线程在启动了两个线程后继续执行并结束,而不会影响或等待这两个子线程的执行。

优点

任务类只是实现接口,可以继续继承其他类、实现其它接口,扩展性强。

 

创建方式三 

通过Callable接口和FutureTask类来创建线程的方法

让我们详细讲解一下Java中通过Callable接口和FutureTask类来创建线程的方法,并以上面提到的类似代码来举例。

Callable接口

Callable接口与Runnable接口类似,都是为了被线程执行而设计的。但与Runnable不同的是,Callable可以返回值,并且它可以抛出异常。这使得Callable接口在某些场景下比Runnable更加灵活和强大。

FutureTask类

FutureTask类实现了FutureRunnable接口。它可以将CallableRunnable对象包装起来,以便有返回值的任务可以被提交给Executor执行。如果任务通过Callable包装,那么FutureTask将返回执行结果;如果通过Runnable包装,那么FutureTaskget()方法将返回null

构造器

  • public FutureTask<>(Callable<V> callable):这个构造器接受一个Callable<V>类型的参数。Callable是一个类似于Runnable的接口,但它可以返回一个结果并且可以抛出一个异常。FutureTask会将这个Callable对象封装成一个可以异步执行的任务。与Runnable不同,Callablecall方法可以有返回值,并且可以声明抛出异常。

方法

  • public V get() throws InterruptedException, ExecutionException:这个方法用于等待计算完成,并检索其结果。如果在计算完成之前调用此方法,它将会阻塞当前线程,直到计算完成。此方法会抛出两种类型的异常:
    • InterruptedException:如果当前线程在等待过程中被中断,则会抛出此异常。
    • ExecutionException:如果计算抛出异常,则会通过此异常包装并抛出。

示例代码

假设我们有一个任务,该任务需要计算一个整数的阶乘,并返回结果。我们可以使用Callable接口来定义这个任务,并使用FutureTask来包装它,以便可以提交给线程池执行。


public class FactorialCallable implements Callable<Long> {  
    private int number;  
  
    public FactorialCallable(int number) {  
        this.number = number;  
    }  
    //重写call方法
    @Override  
    public Long call() throws Exception {  
    //描述线程的任务,返回执行后的结果
        long result = 1;  
        for (int i = 1; i <= number; i++) {  
            result *= i;  
        }  
        return result;  
    }  
}  
  
public class DirectThreadExample {  
    public static void main(String[] args) {  
        // 创建 Callable 任务实例  
        FactorialCallable task = new FactorialCallable(5);  
  
        // 由于 Thread 不能直接执行 Callable,我们需要将 Callable 包装为 FutureTask  
        FutureTask<Long> futureTask = new FutureTask<>(task);  
  
        // 创建一个新的 Thread,并将 FutureTask 作为其任务  
        Thread thread = new Thread(futureTask);  
  
        // 启动线程  
        thread.start();  
  
        try {  
            // 等待任务完成并获取结果  
            Long result = futureTask.get(); // 这会阻塞当前线程直到任务完成  
            System.out.println("Factorial of 5 is: " + result);  
        } catch (InterruptedException | ExecutionException e) {  
            e.printStackTrace();  
        }  
  
        // 注意:在实际应用中,你可能需要处理线程的中断和优雅关闭等逻辑  
    }  
}

优点

线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强。

可以在线程执行完毕后去获取线程执行的结果。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2073673.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数学建模~~~预测方法--决策树模型

目录 0.直击重点 1.决策树概念 2.节点特征的选择算法 3.基尼系数的计算 4.决策树的分类 5.模型的搭建 6.模型的改进和评价 ROC曲线 参数调优 &#xfeff;GridSearch网格搜索 使用搜索结果重新建模 0.直击重点 这个文章&#xff0c;我们从三个维度进行说明介绍&#…

如何使用Python快速修改文件的标签(如何将歌词嵌入到音乐文件中,含歌词嵌入接口源码)

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 Python与Music 📒📝 1. 初探音乐文件的标签📝 使用Python修改标签📝 将歌词嵌入音乐文件⚓️ 相关链接 ⚓️📖 介绍 📖 你是否曾经听过一首好听的歌曲,却发现它的标签信息(元数据信息)杂乱无章?甚至找不到歌词?…

【Remi Pi开发板镜像烧录】使用sd卡进行瑞米派镜像的烧录

烧录大典 按照《软件开发指南》4.2.1和4.2.2的顺序进行&#xff0c;具体烧录哪个镜像结合你自己的需求&#xff0c;每个镜像的区别参考以下链接 https://mbb.eet-china.com/forum/topic/143906_1_1.html Tera term界面全屏如下设置看着比较舒服 设置完之后setup->save-&g…

智能优化特征选择|基于鹦鹉优化(2024年新出优化算法)的特征选择(分类器选用的是KNN)研究Matlab程序 【优化算法可以替换成其他优化方法】

智能优化特征选择|基于鹦鹉优化&#xff08;2024年新出优化算法&#xff09;的特征选择&#xff08;分类器选用的是KNN&#xff09;研究Matlab程序 【优化算法可以替换成其他优化方法】 文章目录 一、PO基本原理PO基本原理基本流程示例应用 二、实验结果三、核心代码四、代码获…

npm pack使用

npm pack 的作用主要是从包中创建一个压缩文件&#xff08;tarball&#xff09;&#xff0c;通常具有.tgz扩展名&#xff0c;包含了打包的模块及其依赖&#xff0c;可用于分发或部署。其应用场景包括私有库或组件的分发、离线环境的依赖安装、CI/CD 自动化构建等。 在使用npm管…

UE管理内容 —— FBX Material Pipeline

目录 Material Support Multiple Materials Material Naming Material Ordering Texture Import FBX管道将应用于网格体&#xff08;静态网格体和骨架网格体&#xff09;的材质和纹理&#xff0c;从3D应用程序传输到虚幻&#xff1b; 要转换简单材质&#xff0c;可以导入源…

Gameplay Ability System(通过GameplayEffect里的Execution修改角色属性)

一、关于GameplayEffectExecutionCalculation类 1、查看GameplayEffectExecutionCalculation类的Execute函数 这个函数将编辑器里设置的参数传进来&#xff0c;然后通过计算再返回出去&#xff0c;这个函数被标记为BlueprintNativeEvent&#xff0c;所以我们可以在自己的类Pla…

python怎么去除换行符

在Python的编写过程中&#xff0c;获取到的字符串进场存在不明原因的换行和空格&#xff0c;如何整合成一个单句&#xff0c;成为问题。 方法&#xff1a; 一、去除空格 “ ”代表的为空格 "xyz".strip() # returns "xyz" "xyz".ls…

ES6 class小挑战

// 编码挑战 #2 /* 重新创建挑战 1&#xff0c;但这次使用 ES6 类&#xff1b; a. 添加一个名为 “speedUS ”的获取器&#xff0c;返回当前速度&#xff08;单位&#xff1a;mi/h&#xff09;&#xff08;除以 1.6&#xff09;&#xff1b; 3. a. 添加一个名为 “speedUS ”…

RM遥控键鼠控制总结

硬件&通信介绍 RM比赛中各个参赛队伍使用的都是大疆官方提供的遥控器套装&#xff0c;包括遥控器和接收机&#xff0c;接收机上共三个引脚&#xff1a;VCC&#xff0c;GND&#xff0c;DBUS&#xff08;数据通道&#xff09;&#xff0c;首次使用需要进行遥控器和接收机配对…

Bootstrap 滚动监听(Scrollspy)插件

滚动监听&#xff08;Scrollspy&#xff09;插件&#xff0c;即自动更新导航插件&#xff0c;会根据滚动条的位置自动更新对应的导航目标。其基本的实现是随着您的滚动&#xff0c;基于滚动条的位置向导航栏添加 .active class。 如果您想要单独引用该插件的功能&#xff0c;那…

快团团团长如何高质量选品?如何做最懂顾客的团长!

精准的用户画像&#xff0c;能够帮助团长个性化推荐&#xff0c;精细化营销&#xff0c;提升消费粘性&#xff0c;秒提下单率。 数据中心 用户画像 用户画像的核心&#xff0c;是为用户打标签。 将用户的每个具体信息抽象成标签&#xff0c;利用这些标签将用户具体化&#…

Docker数据卷使用手册

目录 目标 前言 概念 官方文档 匿名卷&#xff08;Anonymous Volumes&#xff09; 简介 案例 命名卷&#xff08;Named Volumes&#xff09; 简介 案例 目标 掌握Volume命令通过演示案例&#xff0c;理解数据卷种类与各自的用途。 前言 我们在很多网上教程上可以看到…

位数问题c++

题目描述 在所有的N位数中&#xff0c;有多少个数中有偶数个数字3&#xff0c;由于结果可能很大&#xff0c;你只需要输出这个答案对12345取余的值。 输入 读入一个数N(N≤1000) 输出 输出有多少个数中有偶数个数字3。 样例输入 2 样例输出 73样例解释&#xff1a; 1…

【SpringCloud Alibaba】(八)学习 Sentinel 核心技术与配置规则(下)

目录 1. 热点规则1.1 演示热点规则1.2 演示热点高级选项规则 2. 授权规则2.1 演示授权规则 3. 系统规则3.1 演示系统规则 4. SentinelResource 注解4.1 SentinelResource 注解概述4.2 演示 SentinelResource 注解4.2.1 定义限流和降级后的处理方法4.2.2 在外部类中指定限流和异…

Python酷库之旅-第三方库Pandas(098)

目录 一、用法精讲 421、pandas.DataFrame.infer_objects方法 421-1、语法 421-2、参数 421-3、功能 421-4、返回值 421-5、说明 421-6、用法 421-6-1、数据准备 421-6-2、代码示例 421-6-3、结果输出 422、pandas.DataFrame.copy方法 422-1、语法 422-2、参数 …

sheng的学习笔记-AI-半监督学习

AI目录&#xff1a;sheng的学习笔记-AI目录-CSDN博客 基础知识 什么是半监督学习 我们在丰收季节来到瓜田&#xff0c;满地都是西瓜&#xff0c;瓜农抱来三四个瓜说这都是好瓜&#xff0c;然后再指着地里的五六个瓜说这些还不好&#xff0c;还需再生长若干天。基于这些信息&a…

前端面试题-场景设计题

1. 如何使用 css 画一个三角形 借助 border 实现&#xff0c;在 width 和 height 都为 0 时&#xff0c;设置 border&#xff0c;便会呈现三角形。想要哪个方向的三角形&#xff0c;设置其他三边为 透明即可。 同时&#xff0c;可以通过调整不同边的宽度&#xff0c;来调整三角…

巨省成本的电子版招生简章越来越流行,你知道是怎么制作的吗?

随着科技的不断发展&#xff0c;传统的纸质招生简章逐渐被电子版招生简章所取代。电子版招生简章不仅能够节省大量成本&#xff0c;还能够提高宣传效果&#xff0c;因此在市场上越来越流行。你知道它是怎么制作的吗&#xff1f; 1.要制作电子杂志,首先需要选择一款适合自己的软…

Milvus在数派数据大模型业务中的实践

01. 写在前面 云南数派数据科技有限公司是一家总部位于美丽春城昆明的大模型创新企业&#xff0c;在深圳和昆明双城并立&#xff0c;设立了两个研发中心。我们的旗舰产品——SUPIEDT大模型开发平台&#xff0c;作为一款全生命周期的异构算力大模型开发平台&#xff0c;集一站式…