解锁性能:玩转多线程编程的新姿势......

拥有多线程和拥有一百枚核弹没有区别,因为都是毁灭性的存在。——麦克阿瑟

在Java中,实现多线程主要有三种方式:继承Thread类、实现Runnable接口和实现Callable接口。

多线程的形式上实现方式主要有两种,一种是继承Thread类,一种是实现Runnable接口。本质上实现方式都是来实现线程任务,然后启动线程执行线程任务(这里的线程任务实际上就是run方法)。

第一种方式:继承Thread类

万物皆可视为对象,线程也不例外。线程作为对象,具备可抽取的公共特性,这些特性可封装为类。通过使用类,我们可以实例化多个相同特性的对象。Thread类是JDK提供的一种简单方式来实现线程,通过继承Thread类并重写其run方法,我们可以在线程启动时执行自定义的run方法体内容。

在Spring Boot项目中,新创建一个TestThread测试线程类,并继承Thread,然后添加一个当前线程的名称,代码如下:

   public TestThread() {this.setName("测试线程");}

接着重写run方法,来实现相关的业务逻辑,代码如下:

    @Overridepublic void run() {while (true) {System.out.println("线程名:" + Thread.currentThread().getName());try {//线程休眠2秒Thread.sleep(2000);} catch (Exception e) {throw new RuntimeException(e);}}}

然后在项目启动类中main方法中,启动该线程,在启动线程的时候,并不是调用线程类的run方法,而是调用了线程类的start方法。

    public static void main(String[] args) {SpringApplication.run(DemoThreadApplication.class, args);TestThread thread = new TestThread();//使用start,启动线程thread.start();while (true) {System.out.println("线程名:" + Thread.currentThread().getName());//线程休眠2秒try {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}

启动项目,然后在控制台会输出运行的结果:

线程名:main
线程名:测试线程
线程名:测试线程
线程名:main
线程名:测试线程
线程名:main
线程名:测试线程
线程名:main

主线程main和测试线程run方法交替执行,在控制台是随机输出的,原因就是cpu将时间片分给不同的线程,线程获得时间片后就执行任务,所以这些线程在交替的执行输出,导致输出呈现乱序的效果。由此可见,线程开启后不一定会立即执行,则是由cpu调度执行的。

第二种方式:实现Runnable接口

为了避免Java单继承的限制,我们可以选择实现Runnable接口。通过实现Runnable接口的run()方法,我们可以将一个实现了该接口的对象传递给Thread类的构造方法来创建和启动线程。

Runnable接口的源代码如下:

@FunctionalInterface
public interface Runnable {public abstract void run();
}

使用Runnable创建线程的步骤如下:

  1. 定义一个类实现Runnable的接口,作为线程任务类。
  2. 重写run方法,并实现相应的业务代码,也是线程所要执行的代码。
  3. 在启动类的main方法中创建线程任务类。
  4. 创建Thread类,并将线程任务类作为Thread类的构造方法传入,并启动线程。

创建线程任务,每隔2秒执行一次,代码如下:

public class TestRunnable implements Runnable{@Overridepublic void run() {while (true) {System.out.println("TestRunnable线程名:" + Thread.currentThread().getName());try {//线程休眠2秒Thread.sleep(2000);} catch (Exception e) {throw new RuntimeException(e);}}}
}

然后在启动类中创建线程,并将任务交付给线程进行处理,然后启动该线程,代码如下:

public static void main(String[] args) {TestRunnable testRunnable = new TestRunnable();//创建线程对象,通过线程对象来开启的线程new Thread(testRunnable).start();while (true) {System.out.println("线程名:" + Thread.currentThread().getName());try {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}}
}

运行结果如下所示:

线程名:main
TestRunnable线程名:Thread-4
TestRunnable线程名:Thread-4
线程名:main
TestRunnable线程名:Thread-4
线程名:main

实现Runnable接口的输出结果和继承Thread类的结果是一样的,都是交替运行输出, 但是ava是单继承的,就比如我们一个类已经继承了其他的类,就不能使用继承Thread类来创建线程了。但是我们可以使用实现Runnable接口来创建线程。

第三种方式:实现Callable接口

Callable是Java中的一个接口,类似于Runnable,它允许定义一个可以有返回值的任务,并且这个任务可以并发执行。Callable接口和Runnable接口类似,但是它允许返回结果,并能抛出异常。Callable需要依赖FutureTask,用于接收运算结果。一个产生结果,一个拿到结果。FutureTask是Future接口的实现类,也可以用作闭锁。

在使用Callable接口时,需要注意以下几点:

  1. Callable接口的任务可以抛出异常,因此需要在任务中处理异常或者在调用Future.get()方法时处理异常。
  2. Future.get()方法是阻塞的,它会等待任务执行完成并返回结果。如果任务执行时间较长,会影响程序的性能。因此,在使用Future.get()方法时需要谨慎考虑程序的性能和效率。
  3. Callable接口的任务可以并发执行,因此可以在多线程环境下使用,可以使用ExecutorService类来管理线程池并提交任务。

使用Callable创建线程的步骤:

  1. 创建一个类实现Callable接口,并实现call方法。
  2. 创建一个FutureTask,指定Callable对象,做为线程任务。
  3. 创建一个线程,并指定线程任务。
  4. 启动线程。

创建一个TestCallable类,然后实现Callable接口,并实现call方法,代码如下:

public class TestCallable implements Callable {@Overridepublic Integer call() throws Exception {System.out.println("开始执行Callable线程。。。");// 睡1sThread.sleep(1000);return 111;}}

然后在启动类中执行该线程。

public static void main(String[] args) throws ExecutionException, InterruptedException {TestCallable testCallable = new TestCallable();//执行Callable方式,需要FutureTask实现类的支持,FutureTask<Integer> futureTask = new FutureTask(testCallable);//启动线程new Thread(futureTask).start();System.out.println("接收线程运算的结果。。。");Integer result = futureTask.get();System.out.println("线程返回值:" + result);
}

运行结果如下所示:

接收线程运算的结果。。。
开始执行Callable线程。。。
线程返回值:111

三种创建线程的方式比较

  1. Thread
    优点:简单易用,易于理解。可以重写Thread类的run()方法来实现线程的功能,符合面向对象的思想。
    缺点
  • Java不支持多重继承,因此如果已经继承了其他类,则无法再继承Thread类。
  • 每个线程都需要创建新的对象,会消耗更多的内存。
  • 不适合在多线程共享资源的情况下使用,因为不同线程的run()方法内部使用的不是同一个对象,需要额外处理共享资源的问题。
  1. 实现Runnable接口
    优点
  • 可以避免Java单继承的限制,可以与其他类继承。
  • 适合在多线程共享资源的情况下使用,因为多个线程可以共享同一个Runnable对象。
  • 可以将任务代码和资源分离,代码更加清晰。
    缺点
  • 不支持传递参数,如果需要传递参数,需要在Runnable对象中添加属性或者使用外部变量。
  • 不适合在需要返回值的情况下使用,因为Runnable接口没有定义返回值的方法。
  1. 实现Callable接口
    优点
  • 可以定义有返回值的任务,比Runnable更加灵活。
  • 可以抛出异常,适合在需要处理异常的情况下使用。
  • 可以使用Future对象获取任务的结果,比使用Thread.join()方法更加方便。
  • 支持并发执行,可以提高程序的效率和性能。
    缺点
  • 使用比较复杂,需要使用ExecutorService类和Future对象来管理线程和获取任务结果。
  • 不适合在需要大量使用线程并且需要返回结果的场景下使用,因为每个任务都需要创建一个新的Callable对象和Future对象,会消耗更多的内存。

综上所述,应该根据具体的需求和场景选择最合适的方式。如果只需要简单的启动线程并且不需要返回结果,可以选择继承Thread类的方式;如果需要传递参数并且不需要返回结果,可以选择实现Runnable接口的方式;如果需要返回结果并且能够处理异常,可以选择实现Callable接口的方式。

代码地址:https://github.com/dawandou/msy-code中的demo-thread项目。

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

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

相关文章

NX二次开发UF_CAM_ask_lower_limit_plane_status 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;里海NX二次开发3000例专栏 UF_CAM_ask_lower_limit_plane_status Defined in: uf_cam_planes.h int UF_CAM_ask_lower_limit_plane_status(tag_t object_tag, UF_PARAM_lwplane_status_t * status ) overview 概述 Query the…

NX二次开发UF_CAM_ask_opt_template_object 函数介绍

文章作者&#xff1a;里海 来源网站&#xff1a;里海NX二次开发3000例专栏 UF_CAM_ask_opt_template_object Defined in: uf_cam.h int UF_CAM_ask_opt_template_object(UF_CAM_opt_t * opt_object ) overview 概述 This function provides the object which is used to in…

hadoop 编写开启关闭集群脚本, hadoop hdfs,yarn开启关闭脚本。傻瓜式hadoop脚本 hadoop(九)

1. 三台机器&#xff1a; hadoop22, hadoop23, hadoop24 2. hdfs在22机器启动&#xff0c;yarn在hadoop23机器 3. 脚本需要hadoop用户启动才可以 4. 脚本&#xff1a; #!/bin/bashHADOOP_PATH"/opt/module/hadoop-3.3.4"# 检查脚本执行用户是否为 hadoop if [ &q…

〖大前端 - 基础入门三大核心之JS篇㊴〗- DOM节点的关系

说明&#xff1a;该文属于 大前端全栈架构白宝书专栏&#xff0c;目前阶段免费&#xff0c;如需要项目实战或者是体系化资源&#xff0c;文末名片加V&#xff01;作者&#xff1a;不渴望力量的哈士奇(哈哥)&#xff0c;十余年工作经验, 从事过全栈研发、产品经理等工作&#xf…

11、利用大津算法完成一张图片的前景分割

上一篇文章介绍了大津算法,总的来说,大津算法的核心思想就两个: 数学上,通过确定一个像素阈值,来将图片中的像素分为两类,一类前景、一类背景,然后计算两类图像的类间方差,使方差最大。 工程实现上,为了确定像素阈值,采用遍历的方法来实现。 代码实现 下面通过一个…

[C++ 从入门到精通] 12.重载运算符、赋值运算符重载、析构函数

&#x1f4e2;博客主页&#xff1a;https://loewen.blog.csdn.net&#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;本文由 丶布布原创&#xff0c;首发于 CSDN&#xff0c;转载注明出处&#x1f649;&#x1f4e2;现…

VulnHub DC-7

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏…

释放锁流程源码剖析

1 释放锁流程概述 ReentrantLock的unlock()方法不区分公平锁还是非公平锁。 首先调用unlock()方法。 unlock()底层使用的是Sync.release(1)方法 public void unlock() {<!-- --> sync.release(1); } release(1)方法会调用tryRelease(1)去尝试解锁。 public fin…

TC397 EB MCAL开发从0开始系列 之 [15.0] Fee配置说明 -理论详解

一、Fls模块详解1. FEE驱动工作模式:1.1 双扇区&QS1.2 单双扇区1.3 单QS2. Fls配置相关3. 初始化FEE驱动程序4. FEE operation5. Configuration of QS blocks6. Key points to consider6.1 FEE和FLS依赖6.2 GC介绍6.3 写块接近GC阈值6.4 FEE_E_GC_TRIG DEM6.5 Fee_Read和Fe…

EtherCAT从站EEPROM分类附加信息详解:RXPDO(输入过程数据对象)

0 工具准备 1.EtherCAT从站EEPROM数据(本文使用DE3E-556步进电机驱动器)1 分类附加信息——RXPDO(输入过程数据对象) 1.1 分类附加信息规范 在EEPROM字64开始的区域存储的是分类附加信息,这里存储了包括设备信息、SM配置、FMMU配置在内的诸多信息。每个信息在一段连续的…

python实战—核心基础1(高考倒计时)lv1

目录 一、核心代码解释 二、代码 三、运行截图 一、核心代码解释 1、datetime模块 Python有一个名为datetime的模块&#xff0c;用于处理日期和时间。 datetime模块中定义的一个类是datetime类。 可以使用now()方法创建一个包含当前本地日期和时间的datetime对象。 impo…

藏头诗(C语言)

本题要求编写一个解密藏头诗的程序。 注&#xff1a;在 2022 年 7 月 14 日 16 点 50 分以后&#xff0c;该题数据修改为 UTF-8 编码。 输入格式&#xff1a; 输入为一首中文藏头诗&#xff0c;一共四句&#xff0c;每句一行。注意&#xff1a;一个汉字占三个字节。 输出格…

Milvus Standalone安装

使用Docker Compose安装 Milvus standalone&#xff08;即单机版&#xff09;&#xff0c;进行一个快速milvus的体验。 前提条件&#xff1a; 1.系统可以使用centos 2.系统已经安装docker和docker-compose 3.milvus版本这里选择2.3.1 由于milvus依赖etcd和minio&#xff0c…

公司电脑文件透明加密、防泄密管理软件系统

天锐绿盾数据透明加密系统是一款采用驱动层透明加密技术实现电子文件安全加密的防护产品&#xff0c;可以对企业电子文件的存储、访问、传播和处理过程实施全方位保护。该系统遵循基于文件生命周期安全防护的思想&#xff0c;集成了密码学、访问控制和审计跟踪等技术手段&#…

MySQL优化-查询优化

MySQL查询优化是指通过调整查询语句、优化表结构、使用索引等方式&#xff0c;提高查询性能的过程。以下是MySQL查询优化的几种方法&#xff1a; 1. 尽量避免使用SELECT* SELECT *会查询表中的所有列&#xff0c;包括不需要的列&#xff0c;这会消耗大量的计算资源和时间。而…

PTA目录树

在ZIP归档文件中&#xff0c;保留着所有压缩文件和目录的相对路径和名称。当使用WinZIP等GUI软件打开ZIP归档文件时&#xff0c;可以从这些信息中重建目录的树状结构。请编写程序实现目录的树状结构的重建工作。 输入格式: 输入首先给出正整数N&#xff08;≤104&#xff09;…

【开源】基于Vue.js的计算机机房作业管理系统的设计和实现

项目编号&#xff1a; S 017 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S017&#xff0c;文末获取源码。} 项目编号&#xff1a;S017&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 登录注册模块2.2 课程管理模块2.3 课…

某app c++层3处魔改md5详解

hello everybody,本期是安卓逆向so层魔改md5教学,干货满满,可以细细品味,重点介绍的是so层魔改md5的处理. 常见的魔改md5有: 1:明文加密前处理 2:改初始化魔数 3:改k表中的值 4:改循环左移的次数 本期遇到的是124.且循环左移的次数是动态的,需要前面的加密结果处理生成 目录…

crmchat安装搭建教程文档 bug问题调试

一、安装PHP插件&#xff1a;fileinfo、redis、swoole4。 二、删除PHP对应版本中的 proc_open禁用函数。 一、设置网站运行目录public&#xff0c; 二、设置PHP版本选择纯静态。 三、可选项如有需求则开启SSL,配置SSL证书&#xff0c;开启强制https域名。 四、添加反向代理。 …

Docker发布简单springboot项目

Docker发布简单springboot项目 在IDEA工具中直接编写Dockerfile文件 FROM java:8COPY *.jar /app.jarCMD ["--server.prot 8080"]EXPOSE 8080ENTRYPOINT ["java", "-jar", "/app.jar"]将项目打包成对应的jar包&#xff0c;将Dockerf…