(详解版)创建线程的四种方式

文章目录

  • Java中创建线程的四种方式
    • 1. 继承`Thread类`并重写 `run` 方法来创建线程
    • 2. 实现`Runnable接口`并实现 `run` 方法来创建线程。
    • 3. 使用`Callable接口`创建线程
    • 4. 使用`Executor框架`创建线程

Java中创建线程的四种方式


接下来我会详细解释这四种方式创建线程如何实现.

我们如果要创建线程实例,就需要先知道 **线程是什么? 在Java中又是以怎样的形式存在的?**只有了解了这些我们才能更好的理解可以通过多种方式来创建线程.

Q: 线程是什么?

A: 线程(Thread)是计算机科学中的一个基本概念,是进程内的一个独立执行单元。一个进程可以包含多个线程,这些线程共享进程的资源,但拥有各自的执行路径。每个线程都是独立运行的,有自己的程序计数器(Program Counter)、寄存器集合和栈。

线程是程序执行的最小单元,它执行进程中的指令序列。相比于进程,线程的创建和销毁的开销较小,线程间的切换成本也相对较低。多线程的优势在于能够更好地利用多核处理器的性能,以及更有效地进行并发编程。

线程通常有两种模型:用户级线程和内核级线程。

  1. 用户级线程: 用户级线程是由用户空间的线程库(Thread Library)管理的,而不需要操作系统内核的支持。用户级线程的切换由线程库在用户空间完成,相对较快。然而,用户级线程的一个缺点是,如果一个线程发生阻塞,整个进程都会被阻塞,因为内核并不知道线程的存在。
  2. 内核级线程: 内核级线程是由操作系统内核管理的,它直接受操作系统的支持。内核级线程的切换涉及到内核的介入,相对较慢。但是,内核级线程的一个优点是如果一个线程发生阻塞,其他线程仍然可以继续执行。

Q:Java中线程又是以怎样的形式存在的?

A: Java中,线程是通过java.lang.Thread类来表示的。Java提供了多线程的支持,通过继承Thread类或实现Runnable接口,可以创建和管理线程。线程的执行通常通过调用线程的start方法来启动,而线程的实际执行逻辑则由run方法定义。


1. 继承Thread类并重写 run 方法来创建线程

2. 实现Runnable接口并实现 run 方法来创建线程。

由于前两种创建线程的方式比较的简单,所以我们就一起讲了,看了之后的解释,你也就会明白我会什么会把这两种方式一起讲了,因为二者的本质其实没有什么非常大的差别.

结构决定性质,我们首先先初步认识下线程的结构,通过查看Java标准库中的 java.lang.Thread类 中的构造方法,我们就可以初步判断出线程Thread对象创建的两种方式,分别是

1.继承Thread类,使用的是Thread类中的缺省构造器(无参构造方法) .

2. 实现Runnable接口,通过观察可以直到在Thread类中其他有参构造方法几乎都有Runnable这个接口 .

如下图:

image-20231219171728045

  • 那么我们继续思考,是否可以继续扩展?

现在我通过观察Java.lang.Thread类中的构造方法,知道了创建线程至少可以有这两种方法,分别是继承Thread类以及实现Runnable接口,那么我们是否可以继续扩展,由于创建线程实例的目的是重写run方法或者实现run方法,定义线程的执行逻辑。那么我是否可以加上匿名内部类或者Lambda的知识呢?这样的话,创建线程的方式又可以细分.

组合搭配之后创建线程的流程主要包括以下步骤:

  1. 继承 Thread 类:

    • 创建一个继承自 Thread 类的新类。
    • 在新类中重写 run 方法,定义线程的执行逻辑。
    • 创建该类的实例。
    • 调用实例的 start 方法,启动线程。
    class MyThread extends Thread {public void run() {// 线程执行逻辑}
    }// 创建线程的实例
    MyThread myThread = new MyThread();
    // 启动线程
    myThread.start();
    
  2. 实现 Runnable 接口:

    • 创建一个实现 Runnable 接口的类。
    • 在该类中实现 run 方法,定义线程的执行逻辑。
    • 创建 Thread 类的实例,将实现了 Runnable 接口的对象传递给 Thread 构造方法。
    • 调用 start 方法,启动线程。
    class MyRunnable implements Runnable {public void run() {// 线程执行逻辑}
    }// 创建线程的实例
    Thread thread = new Thread(new MyRunnable());
    // 启动线程
    thread.start();
    
  3. 使用匿名内部类:

    • 使用匿名内部类创建线程,同时实现 run 方法。
    • 创建 Thread 类的实例。
    • 调用 start 方法,启动线程。
    Thread thread = new Thread(new Runnable() {public void run() {// 线程执行逻辑}
    });// 启动线程
    thread.start();
    
  4. 使用 Lambda 表达式:

  • 使用 Lambda 表达式创建线程,直接在 Runnable 接口的匿名实现中定义 run 方法。
  • 创建 Thread 类的实例。
  • 调用 start 方法,启动线程。
Thread thread = new Thread(() -> {// 线程执行逻辑
});// 启动线程
thread.start();

这些步骤涵盖了主要的线程创建方式。选择哪种方式取决于任务的性质以及对代码的偏好。无论哪种方式,最终的目标是定义线程的执行逻辑并启动线程。


接下来我们来学习剩下的两个创建线程的方式.

3. 使用Callable接口创建线程

认识Callable

Callable 接口是 Java 中用于表示可调用任务(可以返回结果并抛出异常)的接口。与 Runnable 接口不同,Callablecall 方法可以返回执行结果,而 Runnablerun 方法则没有返回值。通常,Callable 接口结合 Future 接口一起使用,Future 代表一个异步计算的结果,可以通过它来获取任务的执行结果,或者等待任务执行完毕.

  • 帮助文档->接口Callable

image-20231219182436261

  • 帮助文档->FutureTask

image-20231219182917253

我们通过对照对比的方式来学习Callable接口哈,这样能够更好的帮助我们理解Callable接口,光看上面的解释和图片你可能不能第一时间消化,现在我们有一个例子,我们需要使用多线程来模拟一个耗时操作

  • 不使用Callable接口和Future
public class RunnableExample {public static void main(String[] args) {// 使用Runnable创建一个任务Runnable runnableTask = () -> {System.out.println("Executing Runnable task...");// 模拟一个耗时操作try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}};// 创建一个线程来执行Runnable任务Thread thread = new Thread(runnableTask);thread.start();// 此时可以执行一些其他的操作}
}
  • 使用 CallableFuture 的情况
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class CallableExample {public static void main(String[] args) {// 使用Callable创建一个任务Callable<Integer> callableTask = () -> {System.out.println("Executing Callable task...");Thread.sleep(2000); // 模拟一个耗时操作return 42;};// 使用FutureTask包装Callable任务FutureTask<Integer> futureTask = new FutureTask<>(callableTask);// 创建一个线程来执行FutureTaskThread thread = new Thread(futureTask);thread.start();// 此时可以执行一些其他的操作try {// 获取Callable任务的执行结果,此处会阻塞直到任务执行完毕Integer result = futureTask.get();System.out.println("Result: " + result);} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}}
}

通过上述的3两个代码块我们来简单的做个总结:

使用 Callable 的优点:

  1. 可以返回结果: Callable 允许任务返回一个结果,而 Runnable 不支持返回结果。这使得在并发编程中更容易获取任务的执行结果。
  2. 支持异常抛出: Callablecall 方法可以抛出受检查的异常,而 Runnablerun 方法不能。这使得在任务执行过程中发生异常时,更容易捕获和处理异常。
  3. 使用 Future 进行异步操作: Future 接口允许异步地获取任务的执行结果,而不需要等待任务完成。这对于并发编程中需要异步操作的场景非常有用。

总体而言,Callable 接口和 Future 接口的结合,提供了更多的灵活性和控制权,特别是在需要获取任务执行结果、处理异常或进行异步操作的情况下。


4. 使用Executor框架创建线程

Java中的Executor框架是一套用于简化多线程编程的工具和框架。它位于java.util.concurrent包下,提供了一种管理和执行线程的方式,使得开发者能够更轻松地编写并发程序。

Executor框架的主要组件包括以下几个:

  1. Executor接口: 是Executor框架的根接口,定义了一个单一的方法 execute(Runnable command),用于执行传入的任务(实现了Runnable接口的对象)。

  2. ExecutorService接口: 继承自Executor接口,提供了更丰富的任务生命周期管理方法,例如提交任务、获取Future对象、关闭ExecutorService等。常见的实现类有ThreadPoolExecutor

  3. ScheduledExecutorService接口: 继承自ExecutorService接口,支持任务的定时执行和周期性执行。常见的实现类有ScheduledThreadPoolExecutor

  4. Executors工厂类: 提供了一些静态方法,用于创建不同类型的ExecutorService实例,例如创建固定大小的线程池、缓存线程池、单线程线程池等。

以下是一个简单的例子,演示了如何使用Executor框架创建线程池并提交任务:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ExecutorExample {public static void main(String[] args) {// 创建固定大小的线程池ExecutorService executorService = Executors.newFixedThreadPool(2);//这里必须要看懂,后面内容我有给出解释// 提交任务给线程池执行for (int i = 0; i < 5; i++) {final int taskId = i;executorService.execute(() -> {System.out.println("Task " + taskId + " is running on thread " + Thread.currentThread().getName());});}// 关闭线程池executorService.shutdown();}
}

在这个例子中,通过Executors.newFixedThreadPool(2)创建了一个固定大小为2的线程池,然后通过execute方法提交了5个任务给线程池执行。这种方式可以有效地管理线程,使得任务可以并发执行,提高了程序的性能。

其中我们对这行代码进行一个详细的解释:

image-20231219190428504

这段代码创建了一个固定大小为2的线程池,使用了ExecutorService接口,并通过Executors.newFixedThreadPool(2)工厂方法来实现。

  1. ExecutorService接口: ExecutorService是Java Executor框架的一个接口,它扩展了Executor接口,提供更多的方法用于管理线程池和任务执行。

  2. Executors.newFixedThreadPool(2) Executors是一个工具类,提供了一些静态方法用于创建不同类型的ExecutorService实例。newFixedThreadPool(2)是其中一种方法,它创建了一个固定大小为2的线程池。这意味着线程池中最多会同时存在两个线程。

  3. executorService 这是创建的ExecutorService实例的引用,通过该引用可以操作和管理线程池。

综合起来,这行代码的作用是创建了一个固定大小为2的线程池,将其引用赋给executorService。这样,你就可以使用executorService来提交任务,线程池会负责管理这两个线程的生命周期、执行任务和处理任务队列。


结尾

以上的内容就是这篇文章带给大家的内容,我们详细的阐述了Java中创建线程的四种方式,如果有任何的问题或者疑问,非常欢迎大家在评论区评论!!!

  1. executorService 这是创建的ExecutorService实例的引用,通过该引用可以操作和管理线程池。

综合起来,这行代码的作用是创建了一个固定大小为2的线程池,将其引用赋给executorService。这样,你就可以使用executorService来提交任务,线程池会负责管理这两个线程的生命周期、执行任务和处理任务队列。


以上的内容就是这篇文章带给大家的内容,我们详细的阐述了Java中创建线程的四种方式,如果有任何的问题或者疑问,非常欢迎大家在评论区评论!!!
在这里插入图片描述

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

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

相关文章

【HarmonyOS开发】ArkTs关系型和非关系型数据库的存储封装

前面使用了首选项的存储方式&#xff0c;因此将其他的两种存储方式&#xff08;键值型数据库和关系型数据库&#xff09;也学习一下&#xff0c;简单记录一下&#xff0c;并进行封装&#xff0c;方便后续使用。 1、效果预览 2、使用条件 2.1 键值型数据库 键值型数据库实现数据…

RabbitMQ入门指南(三):Java入门示例

专栏导航 RabbitMQ入门指南 从零开始了解大数据 目录 专栏导航 前言 一、AMQP协议 1.AMQP 2.Spring AMQP 二、使用Spring AMQP实现对RabbitMQ的消息收发 1.案例准备阶段 2.入门案例&#xff08;无交换机&#xff09; 3.任务模型案例&#xff08;Work Queues&#xff0…

九.数据处理之增删改

数据处理之增删改 1.插入数据1.1实际问题1.2方式1&#xff1a;VALUES的方式添加1.3方式2&#xff1a;将查询结果插入到表中 2.更新数据3.删除数据4.MySQL8新特性&#xff1a;计算列5.综合案例 1.插入数据 1.1实际问题 解决方式&#xff1a;使用INSERT语句向表中插入数据 1.2方…

文献速递:生成对抗网络医学影像中的应用——用于生成前列腺MR-only影像治疗剂量规划的合成CT的深度学习模型:多中心研究

文献速递&#xff1a;生成对抗网络医学影像中的应用——用于生成前列腺MR-only影像治疗剂量规划的合成CT的深度学习模型&#xff1a;多中心研究 本周给大家分享文献的主题是生成对抗网络&#xff08;Generative adversarial networks, GANs&#xff09;在医学影像中的应用。文…

电源模块测试方法 | 怎么测试电源负载瞬态响应?

负载瞬态响应测试是检测电源稳定性和质量的重要方法之一&#xff0c;而电源稳定性是设备正常运行的基础。通过负载瞬态响应测试来检测电源的响应速度和稳定性&#xff0c;从而优化电源设计&#xff0c;提升性能&#xff0c;确保电子设备可以稳定工作。 什么是负载瞬态响应测试?…

js知识点1:防抖节流

js知识点1&#xff1a;防抖节流 防抖节流 防抖节流&#xff0c;本质上是优化高频率执行代码的一种手段 定义&#xff1a; 防抖: n 秒后再执行该事件&#xff0c;若在 n 秒内被重复触发&#xff0c;则重新计时 节流: n 秒内只运行一次&#xff0c;若在 n 秒内重复触发&#xff0…

【C语言】SCU安全项目2-BufBomb

目录 关键代码解读&#xff1a; getxs() getbuf() test() 核心思路 具体操作1 具体操作2 前段时间忙于强网杯、英语4级和一些其他支线&#xff0c;有点摸不清头绪了&#xff0c;特别是qwb只有一个输出&#xff0c;太过坐牢&#xff0c;决定这个安全项目做完后就继续投身…

Spring MVC框架支持RESTful,设计URL时可以使用{自定义名称}的占位符@Get(“/{id:[0-9]+}/delete“)

背景&#xff1a;在开发实践中&#xff0c;如果没有明确的规定URL&#xff0c;可以参考&#xff1a; 传统接口 获取数据列表,固定接口路径&#xff1a;/数据类型的复数 例如&#xff1a;/albums/select RESTful接口 - 根据ID获取某条数据&#xff1a;/数据类型的复数/{id} - 例…

在Linux安装的Docker中开启IPv6

先在Linux中安装docker&#xff0c;然后在docker中开启IPv6。 安装docker 第一步&#xff0c;卸载旧版本docker。 若系统中已安装旧版本docker&#xff0c;则需要卸载旧版本docker以及与旧版本docker相关的依赖项。 命令&#xff1a;yum -y remove docker docker-client do…

CountDownLatch和Semaphore的区别?

CountDownLatch和Semaphore都是在Java中用于多线程协同的工具&#xff0c;但它们有一些重要的区别。 CountDownLatch&#xff1a; 用途&#xff1a; 主要用于等待一个或多个线程完成操作&#xff0c;它的计数器只能被减少&#xff0c;不能被增加。计数&#xff1a; 初始化时需…

栈-二叉树的中序遍历/easy

二叉树的中序遍历 1、题目2、解题思路3、复杂度最优解示例4、抽象与扩展 1、题目 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,3,2]示例 2&#xff1a; 输入&#xff1a;roo…

科聪控制系统典型应用车型 —— 料箱机器人

料箱机器人即料箱AGV是一种智能化物流搬运设备&#xff0c;它可以代替人力完成出库入库和搬运工作&#xff0c;可根据出入库生产出货需求&#xff0c;将货物从起点运送到终点&#xff0c;自动柔性完成货到人货到点的操作。 提升仓储和物流效率的自动化利器 料箱机器人的投用能…

STM32_通过Ymodem协议进行蓝牙OTA升级固件教程

目录标题 前言1、OTA升级的重要性和应用场景2、理论基础2.1、单片机的启动流程2.2、什么是IAP&#xff1f;2.3、什么是OTA&#xff1f;2.4、什么是BootLoader&#xff1f;2.5、Ymodem协议是什么&#xff1f;2.6、IAP是如何实现的&#xff1f; 3、具体操作3.1、软硬件工具准备3.…

数据可视化---直方图

内容导航 类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统…

短剧分销平台搭建,助力普通人进入短剧市场

当下短剧抢占了各大用户的碎片化时间&#xff0c;成为了当代年轻人的“电子榨菜”&#xff0c;目前我国的短剧用户已达到了10.12亿人&#xff0c;拥有庞大的用户体量。 以小程序为主的付费短剧&#xff0c;今年充值流水达到了180多亿元。以当前短剧市场规模计算&#xff0c;今年…

Kafka为什么能高效读写数据

1&#xff09;Kafka 本身是分布式集群&#xff0c;可以采用分区技术&#xff0c;并行度高&#xff08;生产消费方并行度高&#xff09;&#xff1b; 2&#xff09;读数据采用稀疏索引&#xff0c;可以快速定位要消费的数据&#xff1b; 3&#xff09;顺序写磁盘&#xff1b; …

航空港务数据大屏为航空港的可持续发展提供有力支撑!

随着经济的发展&#xff0c;不断加建与扩建民用机场&#xff0c;空港行业规模不断扩大。在不断引进和消化发达国家先进技术的同时&#xff0c;中国深入开展了对新技术和新材料的研究&#xff0c;极大地丰富和发展了中国的机场建设技术。且各项机场建设计划均已落实推进&#xf…

TCAX特效字幕保姆入门教程+效果演示+软件源码自取

目录 介绍 下载链接 初步使用 软件使用 tcc文件介绍 tcc文件版本 模式设置 ​编辑 k值提供方式举例 特效脚本设置 主要设置 ass全局风格设置 额外设置 常见问题 编码使用 使用其他tcax博主的进行编码测试 介绍 TCAX是一款专门用于制作特效字幕的软件。通过TCAX…

Potplayer播放器远程访问群晖WebDav本地资源【内网穿透】

文章目录 本教程解决的问题是&#xff1a;按照本教程方法操作后&#xff0c;达到的效果是&#xff1a;1 使用环境要求&#xff1a;2 配置webdav3 测试局域网使用potplayer访问webdav3 内网穿透&#xff0c;映射至公网4 使用固定地址在potplayer访问webdav 国内流媒体平台的内容…

新版Android Studio Logcat 筛选日志

下载了新版的Android Studio&#xff0c;android-studio-2022.3.1.21-mac_arm&#xff0c;记录一下新版本AS的logcat过滤日志条件 1. 按照包名过滤 1.1 过滤当前包名的日志 package:mine 1.2 过滤其他包名日志 package:com.example.firstemptyapplication 2. 按照日志等级过滤…