多线程的创建方式以及及Thread类详解

目录

一.线程的创建方法:(重点)

        一:继承Thread类

写法一:正常写法

写法二:匿名内部类 

二.实现Runnable接口 

写法一:正常写法

 写法二:匿名内部类 

三. 实现 Callable 接口 

​编辑

四.通过线程池创建线程

二.Thread类及常⻅⽅法

1.Thread的常⻅构造⽅法

2.Thread的⼏个常⻅属性

3.Thread常见方法(重点)

run()方法 和start()方法(区别)

join()方法

 中断线程

sleep()方法 


一.线程的创建方法:(重点)

        一:继承Thread类

写法一:正常写法

  1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务

  2. 创建Thread子类的实例,即创建了线程对象

  3. 调用线程对象的start()方法来启动该线程

public class MyThread  extends  Thread{@Overridepublic void run() {while (true) {System.out.println("这里是子线程");}}public static void main(String[] args) {MyThread thread1 = new MyThread();thread1.start();while (true) {System.out.println("这里是主线程");}}
}

 

写法二:匿名内部类 

    public static void main(String[] args) {Thread thread = new Thread() {@Overridepublic void run() {while (true) {System.out.println("这里是thread线程");}}};thread.start();while (true) {System.out.println("这里是main线程");}}


二.实现Runnable接口 

写法一:正常写法

  1. 创建一个实现了Runnable接口的类,并实现接口中的run()方法,定义线程的执行逻辑。
  2. 在主线程中创建Runnable实例,并将其作为参数传递给Thread类的构造方法。
  3. 调用Thread对象的start()方法启动线程。
public class MyRunnable implements Runnable{@Overridepublic void run() {while (true) {System.out.println("thread!");}}public static void main(String[] args) {MyRunnable runnable = new MyRunnable();Thread thread = new Thread(runnable);thread.start();while (true) {System.out.println("main!");}}
}

 

 写法二:匿名内部类 

    public static void main(String[] args) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {while (true) {System.out.println("thread!");}}});thread.start();while (true) {System.out.println("main!");}}

三. 实现 Callable 接口 

前两种方式都存在一个问题:重写的run方法均不能直接返回结果,不适合需要返回线程执行结果的场景。而通过实现Callable接口则可以做到这一点。

  1. 得到任务对象定义类实现Callable接口,重写call方法,封装要做的事情,用FutureTask把Callable对象封装成线程任务对象
  2. 把线程任务对象交给Thread处理
  3. 调用Thread的start方法启动线程,执行任务
  4. 线程执行完毕后、通过FutureTask的get方法去获取任务执行的结果
public class MyCallable implements Callable<String> {@Overridepublic String call() throws Exception {for (int i = 0; i < 3; i++) {System.out.println("thread:" + i);}return "call!";}public static void main(String[] args) throws ExecutionException, InterruptedException {MyCallable myCallable = new MyCallable();FutureTask<String> futureTask = new FutureTask<>(myCallable);Thread thread = new Thread(futureTask);thread.start();for (int i = 0; i < 3; i++) {System.out.println("main:"+i);}System.out.println(futureTask.get());}
}


四.通过线程池创建线程

后序再详讲


二.Thread类及常⻅⽅法

1.Thread的常⻅构造⽅法

  • Thread t1 = new Thread();
  • Thread t2 = new Thread(new MyRunnable());
  • Thread t3 = new Thread("这是我的名字");
  • Thread t4 = new Thread(new MyRunnable(), "这是我的名字");

2.Thread的⼏个常⻅属性

  •  ID是线程的唯⼀标识,不同线程不会重复
  • 名称是各种调试⼯具⽤到
  •  状态表⽰线程当前所处的⼀个情况,下⾯我们会进⼀步说明
  •  优先级⾼的线程理论上来说更容易被调度到
  •  关于后台线程,需要记住⼀点:JVM会在⼀个进程的所有⾮后台线程结束后,才会结束运⾏。
  • 是否存活,即简单的理解,为run⽅法是否运⾏结束了
  • 线程的中断问题,下⾯我们进⼀步说明 

3.Thread常见方法(重点)

run()方法 和start()方法(区别)

  • start()方法是Thread类中的一个方法,用于启动一个新的线程。当调用start()方法时,系统会创建一个新的线程,并在新的线程中执行run()方法的内容。start()方法会在新的线程中执行一些准备工作,然后调用run()方法。
  • run()方法是实现了Runnable接口的类中的一个方法。在启动一个线程后,系统会自动调用该线程对象的run()方法。run()方法中包含了线程的主体代码,即线程要执行的任务。

2.start()方法

public class MyThread extends Thread{@Overridepublic void run() {while (true) {System.out.println("这里是thread");}}public static void main(String[] args) {MyThread thread = new MyThread() ;thread.start();while (true) {System.out.println("这里是main线程");}}
}

此时为俩个线程并发处理

1.run()方法

 public class MyThread extends Thread{@Overridepublic void run() {while (true) {System.out.println("这里是thread");}}public static void main(String[] args) {MyThread thread = new MyThread() ;thread.run();while (true) {System.out.println("这里是main线程");}}
}

此时只实现了run方法 并没有并发处理 

总结:

  • start() 方法 用于启动一个新线程,它会触发线程的创建和调度,最终由操作系统负责执行 run() 方法。
  • run() 方法 是线程的实际执行任务,是线程执行的代码部分。如果直接调用 run() 方法,它会在当前线程中执行,并不会启动新的线程。

join()方法

在多个线程中,他们的执行顺序属于系统调动的,是无序的(抢占式执行)

而我们希望一个稳定的顺序,如何执行?

join() 方法的使用场景:

场景 1:确保多个线程按顺序执行

有时,你可能希望在主线程中启动多个子线程,并确保在继续执行后续任务之前,所有子线程已经完成。例如,在处理多个任务并且它们之间有依赖关系时,必须等待所有线程完成。

场景 2:等待线程完成后汇总结果

在多线程并发计算时,主线程可能需要等待多个线程完成任务,才能进行汇总或处理结果。join() 方法可以帮助你等待所有线程执行完成后再进行下一步操作。

场景 3:避免主线程提前退出

如果没有使用 join(),主线程可能会在子线程完成之前就退出,导致子线程未执行完成。使用 join() 可以保证主线程在所有子线程完成之前不会退出。

 

案例:

class MyThread extends Thread {private String name;public MyThread(String name) {this.name = name;}public void run() {System.out.println(name + " 正在执行...");try {Thread.sleep(1000); // 模拟任务执行} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name + " 执行完毕!");}
}public class Main {public static void main(String[] args) throws InterruptedException {MyThread thread1 = new MyThread("线程1");MyThread thread2 = new MyThread("线程2");MyThread thread3 = new MyThread("线程3");thread1.start();thread2.start();thread3.start();// 主线程等待所有子线程执行完毕thread1.join();thread2.join();thread3.join();System.out.println("所有线程都已执行完毕,主线程继续执行...");}
}

  • 在这个例子中,主线程通过调用 thread1.join()thread2.join() 和 thread3.join() 来等待所有子线程执行完毕。
  • 主线程会阻塞,直到 thread1thread2 和 thread3 执行完成。
  • 当所有线程执行完毕后,主线程输出 "所有线程都已执行完毕,主线程继续执行..."

总结:

  1. 确保线程顺序:有时我们希望线程按照特定的顺序执行。使用 join() 可以让主线程等待子线程执行完毕,这样就能保证主线程在子线程之后才继续执行。

  2. 避免主线程提前退出:在多线程应用中,如果主线程没有等待子线程完成就退出,可能导致子线程未完成时程序就结束了,特别是在没有使用 join() 时,主线程可能比子线程先结束。

  3. 汇总线程结果:如果多个线程在并行执行,主线程可能需要等待它们执行完毕后再进行结果汇总。join() 方法能够确保线程完成后,主线程能够获取到所有线程的执行结果。

  4. 避免线程竞态条件:通过确保线程按顺序执行,join() 可以避免一些可能的竞态条件和线程同步问题。


 中断线程

在 Java 多线程编程中,中断线程是一种用于请求线程停止执行的机制。线程的中断并不意味着直接停止线程的执行,而是给线程发送一个中断信号,让线程有机会在合适的地方处理这个信号并终止。线程中断通常用于协调多个线程的生命周期,特别是在需要停止一个线程的执行时,或者是某个线程的操作不再需要时

线程中断的使用场景:

  1. 停止正在执行的线程: 当线程正在进行耗时的任务时,如果你需要在某个时刻停止它,可以通过中断来实现。中断信号告诉线程它可以停止工作或清理资源并退出。

    应用场景:比如有一个后台线程在执行某些长时间的计算任务或下载操作,如果用户取消了操作,你可以通过中断线程来停止这些任务。

  2. 响应外部中断请求: 比如在一个多线程的客户端应用中,如果用户要求退出程序或者停止某些操作,可以通过发送中断信号来优雅地停止正在运行的线程。

    应用场景:比如在一个多线程的 HTTP 请求处理系统中,如果服务器需要停止处理请求,可以通过中断正在处理请求的线程。

  3. 在阻塞操作中响应中断: 当线程执行如 sleep()join()wait() 等阻塞操作时,可以中断线程来提前终止阻塞操作,从而使线程能够继续执行其他任务。

    应用场景:在多线程的网络编程中,可能需要等待其他线程的响应,如果等待超时,则可以通过中断等待线程,防止程序长时间处于等待状态。

  4. 取消正在执行的任务: 在一些任务执行框架中,可以通过中断来取消正在执行的任务。当任务执行时间过长,或者任务本身不再需要时,可以通过中断来提示线程终止。

    应用场景:比如在处理大量数据时,当用户主动取消任务时,可以中断相关的线程。

 案例1:使用 Thread.interrupt() 中断线程

public class InterruptExample {public static void main(String[] args) {// 创建线程Thread thread = new Thread(() -> {try {System.out.println("线程开始执行");// 模拟一个长时间的任务for (int i = 0; i < 10; i++) {if (Thread.currentThread().isInterrupted()) {System.out.println("线程被中断,退出循环");return;}Thread.sleep(1000);  // 模拟工作System.out.println("线程正在工作,执行第 " + (i + 1) + " 次");}} catch (InterruptedException e) {System.out.println("线程在阻塞时被中断:" + e.getMessage());}});thread.start();try {// 等待一会儿,然后中断线程Thread.sleep(3000);thread.interrupt();  // 请求中断线程} catch (InterruptedException e) {e.printStackTrace();}}
}

示例 2:线程阻塞时的中断 

public class InterruptInSleepExample {public static void main(String[] args) {Thread thread = new Thread(() -> {try {System.out.println("线程开始执行,准备休眠");Thread.sleep(5000);  // 模拟长时间的阻塞操作System.out.println("线程正常执行完毕");} catch (InterruptedException e) {System.out.println("线程被中断,异常信息:" + e.getMessage());}});thread.start();try {// 等待 2 秒钟然后中断线程Thread.sleep(2000);thread.interrupt();  // 请求中断线程} catch (InterruptedException e) {e.printStackTrace();}}
}

总结

  • 中断线程是通过 Thread.interrupt() 方法发出请求,线程可以通过检查中断状态来响应这个请求。
  • 常见的使用场景包括:停止长时间运行的任务、在阻塞操作中响应外部请求、取消正在进行的操作等。
  • 线程中断是一种“协作式的停止”,即线程需要在合适的地方主动检查中断状态并退出。

sleep()方法 

在Java中,sleep() 方法是 Thread 类中的一个静态方法,用于暂停当前执行的线程指定的时间

案例:定时打印信息

public class TimerTaskExample {public static void main(String[] args) {Long start = System.currentTimeMillis();for (int i = 1; i <= 5; i++) {System.out.println("第 " + i + " 次执行");try {// 每次执行后休眠1秒Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}Long end = System.currentTimeMillis();System.out.println("总共耗时:"+(end-start));}
}


结语: 写博客不仅仅是为了分享学习经历,同时这也有利于我巩固知识点,总结该知识点,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进。同时也希望读者们不吝啬你们的点赞+收藏+关注,你们的鼓励是我创作的最大动力! 

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

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

相关文章

成功解决WSL2上的Ubuntu22.04执行sudo apt-get update指令报错问题

问题&#xff1a;输入sudo apt-get update指令会显示如下报错 问题所在&#xff1a;Temporary failure in name resolution 显然是系统无法解析域名。这可能是 DNS 配置问题。 解决方案&#xff1a; 临时修改 DNS 配置 尝试手动修改 /etc/resolv.conf 文件来使用公共 DNS 服务…

【英特尔IA-32架构软件开发者开发手册第3卷:系统编程指南】2001年版翻译,2-19

文件下载与邀请翻译者 学习英特尔开发手册&#xff0c;最好手里这个手册文件。原版是PDF文件。点击下方链接了解下载方法。 讲解下载英特尔开发手册的文章 翻译英特尔开发手册&#xff0c;会是一件耗时费力的工作。如果有愿意和我一起来做这件事的&#xff0c;那么&#xff…

ffmpeg 视频滤镜:屏蔽边框杂色- fillborders

滤镜描述 fillborders 官网链接 > FFmpeg Filters Documentation fillborders滤镜有几种方式帮你屏蔽边框的杂色、不好的图案。 滤镜使用 参数 left <int> ..FV.....T. set the left fill border (from 0 to INT_MAX) (default 0)right …

Unity性能优化 -- 性能分析工具

Stats窗口Profiler窗口Memory Profiler其他性能分析工具&#xff08;Physica Debugger 窗口&#xff0c;Import Activity 窗口&#xff0c;Code Coverage 窗口&#xff0c;Profile Analyzer 窗口&#xff0c;IMGUI Debugger 窗口&#xff09; Stats 统级数据窗口 game窗口 可…

【Ant.designpro】上传图片

文章目录 一、前端二、后端 一、前端 fieldProps:可以监听并且获取到组件输入的内容 action{“/api/upload_image”} 直接调用后端接口 <ProFormUploadButtonlabel{"上传手续图片"}name{"imgs"}action{"/api/upload_image"}max{5} fieldPro…

王珊数据库系统概论第六版PDF+第五版课后答案+课件

为了保持科学性、先进性和实用性&#xff0c; 编者在第5版教材基础上对全书内容进行了修改、更新和充实。在科学性方面&#xff0c; 编者在系统篇中增加了第9章关系数据库存储管理&#xff0c; 讲解数据库的逻辑与物理组织方式及索引结构。增加这部分内容有助于学生更好地理解关…

胡闹厨房练习(一)

参考教程 参考教程 学习视频 目录 准备工作 一、创建项目 二、处理预置文件和设置 三、编辑器布局 四、Visual Studio设置 五、导入资源 六、设置源素材 七、视觉效果&#xff08;后期处理&#xff09; 角色控制器 一、角色 二、制作动画 三、动画控制 四、运动脚…

「QT」几何数据类 之 QVector2D 二维向量类

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「QT」QT5程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasolid…

Spring中的过滤器和拦截器

Spring中的过滤器和拦截器 一、引言 在Spring框架中&#xff0c;过滤器&#xff08;Filter&#xff09;和拦截器&#xff08;Interceptor&#xff09;是实现请求处理的两种重要机制。它们都基于AOP&#xff08;面向切面编程&#xff09;思想&#xff0c;用于在请求的生命周期…

网站架构知识之Ansible模块(day021)

1.Ansible模块 作用:通过ansible模块实现批量管理 2.command模块与shell模块 command模块是ansible默认的模块&#xff0c;适用于执行简单的命令&#xff0c;不支持特殊符号 案列01&#xff0c;批量获取主机名 ansible all -m command -a hostname all表示对主机清单所有组…

requestAnimationFrame与setInterval的抉择

&#x1f64c; 如文章有误&#xff0c;恳请评论区指正&#xff0c;谢谢&#xff01; ❤ 写作不易&#xff0c;「点赞」「收藏」「转发」 谢谢支持&#xff01; 背景 在之前的业务中遇到有 JS 动画的实现场景&#xff0c;但当电脑打开太多网页或是同时启动很多应用时&#xff0c…

高性能分布式缓存Redis-分布式锁与布隆过滤器

一、分布式锁 我们先来看一下本地锁 在并发编程中&#xff0c;我们通过锁&#xff0c;来避免由于竞争而造成的数据不一致问题。通常&#xff0c;我们以 synchronized 、Lock 来使用它&#xff08;单机情况&#xff09; 来看这段代码 Autowired RedisTemplate<String,Str…

Flutter运行App时出现“Running Gradle task ‘assembleDebug“问题解决

在参考了众多解决办法中最有用并且最快的方法 Gradle Distributions 在这个地方下载对应你这个文件中的gradle版本 然后将 最后一行本来不是这样的,我们把下载好的zip包保存到本地,然后用这个代替网址,最后成功运行

【CUDA】认识CUDA

目录 一、CUDA编程 二、第一个CUDA程序 三、CUDA关键字 四、device管理 4.1 初始化 4.2 Runtime API查询GPU信息 4.3 决定最佳GPU CUDA C 编程指南CUDA C在线文档&#xff1a;CUDA C 编程指南 CUDA是并行计算的平台和类C编程模型&#xff0c;能很容易的实现并行算法。只…

【优选算法篇】微位至简,数之恢宏——解构 C++ 位运算中的理与美

文章目录 C 位运算详解&#xff1a;基础题解与思维分析前言第一章&#xff1a;位运算基础应用1.1 判断字符是否唯一&#xff08;easy&#xff09;解法&#xff08;位图的思想&#xff09;C 代码实现易错点提示时间复杂度和空间复杂度 1.2 丢失的数字&#xff08;easy&#xff0…

从0开始学习机器学习--Day21--算法的评估标准

准确率和召回率(precision and recall) 在上一章我们提到了在每次运行算法时通过返回一个实数值来判断算法的好坏&#xff0c;但是我们该如何构建这个实数的计算公式呢&#xff0c;毕竟这关乎于我们对算法的判断&#xff0c;不能过于夸大或贬低。有一个典型的会被影响的很大例…

自然语言处理在客户服务中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 自然语言处理在客户服务中的应用 自然语言处理在客户服务中的应用 自然语言处理在客户服务中的应用 引言 自然语言处理概述 定义…

【Ubuntu24.04】从双系统到虚拟机再到单系统的故事

故事 在大学前期&#xff0c;我使用Ubuntu系统都是为了学习一些命令或者其它Linux的东西&#xff0c;对性能的要求不高&#xff0c;所以选择了虚拟机&#xff0c;后来为了做毕设&#xff0c;选择安装了Ubuntu20.04双系统&#xff0c;因为虚拟机实在带不动&#xff0c;那时我的主…

初次体验Tauri和Sycamore(1)

原创作者&#xff1a;庄晓立&#xff08;LIIGO&#xff09; 原创时间&#xff1a;2024年11月10日 原创链接&#xff1a;https://blog.csdn.net/liigo/article/details/143666827 版权所有&#xff0c;转载请注明出处。 前言 Tauri 2.0发布于2024年10月2日&#xff0c;Sycamore…

【统计子矩阵——部分前缀和+双指针】

题目 代码 #include <bits/stdc.h> using namespace std; typedef long long ll; const int N 510; int s[N][N]; int main() {ios::sync_with_stdio(0);cin.tie(0);int n, m, k;cin >> n >> m >> k;for(int i 1; i < n; i)for(int j 1; j <…