Javaee---多线程(一)

在这里插入图片描述

文章目录

  • 1.线程的概念
  • 2.休眠里面的异常处理
  • 3.实现runnable接口
  • 4.匿名内部类子类创建线程
  • 5.匿名内部类接口创建线程
  • 6.基于lambda表达式进行线程创建
  • 7.关于Thread的其他的使用方法
    • 7.1线程的名字
    • 7.2设置为前台线程
    • 7.3判断线程是否存活
  • 8.创建线程方法总结
  • 9.start方法
  • 10.终止(中断)线程-interrupt
    • 10.1自定义标志位
    • 10.2使用系统里面的标志位
  • 11.线程等待join
  • 12.线程方法总结
  • 11.线程等待join
  • 12.线程方法总结

1.线程的概念

(下面的这个图片里面的代码有错误,我们new的这个mythread类的对象,应该是使用这个Thread这个类进行接收,这个是个向上转型的过程);

  • 我们的这个里面使用到了这个thread类,但是不需要进行这个import的操作,这个主要就是因为我们的这个Thread是位于这个java.lang这个包下面的;像我们之前使用的这个String也是在这个包下面,我们也没有进行这个import的操作;
  • 这个Thread里面是有这个run方法的,我们的这个mythread类里面对于这个夫类里面的方法进行了重写;
  • 我们的main方法里面执行这个start时候,就会进入这个run入口进而对于这个内容进行打印输出;

image-20241019192827539

实际上打印输出的时候main方法里面的内容和我们的自定义类里面的这个内容是独立进行的,两个并不会相互依赖;每一个线程都是一个独立的执行流;

image-20241019211712898

当我们把这个start修改为这个run之后,这个就会先去执行我们自己的自定义类里面的这个重写的run方法,这个执行完成之后才会继续执行这个下面的helli main打印输出,但是因为我们的这个是死循环,因此这个会一直打印输出我们的hello thread;

image-20241019211737878

2.休眠里面的异常处理

为了更加清楚的看到这个打印的效果,我们可以对于这个程序进行休眠操作,就是使用这个sleep方法,但是这个方法在进行使用的时候可能会出现一些问题,就是异常情况,针对于自定义类和main方法里面的异常,我们的处理手段是不一样的:

下面的这个异常是受查异常,需要进行显示处理:要么throws,要么try-catch

重写父类的方法,父类的方法里面没有throws,因此我们不可以throws;

image-20241020212336753

image-20241020212513823

相比之下,在这个main里面的这个内容都是我们自己写的,因此这个时候我们就可以使用这个throws方法对于这个出现的异常进行抛出;

image-20241020213545037

有了上面的两个休眠的操作,这个时候我们重新运行这个程序,这个时候就会发现这个两个语句的打印输出的速度就变慢了,但是这个出现的先后顺序其实并不是确定的,这个主要取决于我们的操作系统对于这个线程的调度(调度器)的具体实现,因此我们可以看到有的时候是这个hello thread先打印输出,但是有的时候是这个hello main先打印输出;

3.实现runnable接口

下面的这个就是实现我们的这个runnable接口的方式,两个类里面的这个异常的处理方式还是一样的,一个是捕捉异常,一个就是抛出异常;

但是在这个主方法里面,我们使用这个已有的MyRunnable类实例化对象,相当于是定义了一个任务,然后我们吧这个传递到我们的这个new Thread这个参数里面去,相当于就是使用这个Thread去执行这个任务,其他的和上面的这个方式是没有任何区别的;

针对于这个方式,实现了这个runnable接口的这个类的实例化对象作为我们的这个Thread接口的构造方法的参数,这个和上面最大的不同就是解耦合:耦合指的就是我们的一个程序里面的不同模块之间的影响的程序,解耦合就是解除不同的模块之间的相互的影响;

这样的解耦合的好处就是我们只是定义了这个任务,但是交给谁去执行这个任务就没那么重要了,这个就是这个实现接口的方式和上面的最大不同;
在这里插入图片描述

4.匿名内部类子类创建线程

public class Test {public static void main(String[] args) throws InterruptedException {//匿名内部类,不知道这个类叫什么,但是知道这个类是我们的Thread的子类//不知道这个类的名字也不重要,因为这个类我们只会使用一次//下面的这个new后面的这个其实就可以理解为是一个类,使用这个没有名字的类实例化对象,在这个匿名的类里面对于这个方法进行重写,里面的这个对于异常的处理和定义两个类的时候是一样的;Thread t = new Thread(){@Overridepublic void run() {while(true){System.out.println("hello thread!");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}};//使用我们创建的这个线程去开始这个线程的执行,start就是这个线程执行的入口t.start();while(true){System.out.println("hello main!");Thread.sleep(1000);}}
}

5.匿名内部类接口创建线程

public class Test {public static void main(String[] args) throws InterruptedException {//匿名内部类,不知道这个类叫什么,但是知道这个类是我们的Thread的子类//不知道这个类的名字也不重要,因为这个类我们只会使用一次//下面的这个就是我们不知道这个类叫什么名字,但是这个类实现了这个接口Runnable//因此在这个匿名内部类里面对于这个接口里面的方法进行了重写Runnable runnable = new Runnable() {@Overridepublic void run() {while(true){System.out.println("hello thread!");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}};//这个就是我们的这个实现接口的类的实例化对象作为一个参数进行传递Thread t=new Thread(runnable);t.start();while(true){System.out.println("hello main!");Thread.sleep(1000);}}
}

6.基于lambda表达式进行线程创建

lambda实际上是这个匿名内部类的一个简化的方式:本质上就是一个函数表达式

public class test {public static void main(String[] args) throws InterruptedException {//lambda表达式是一个匿名函数,主要是实现回调函数的效果//回调函数是计算机里面的一个很重要的术语//回调函数--函数指针:函数指针实现转移表,减少if-else分支数目//使用函数指针作为回调函数--实现qsort()的比较//其实这个lambda表达式并不是很复杂,形式上就是这个()->{},我们的这个相关的代码就是写在这个大括号			里面的Thread t = new Thread(()->{while(true){System.out.println("hello thread!");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();while(true){System.out.println("hello main!");Thread.sleep(1000);}}
}

7.关于Thread的其他的使用方法

上面我们介绍两个:

1.直接调用无参数的,就是我们最开始介绍的;

2.传递的参数是我们的实现接口的类的实例化对象;

除此之外,我们还可以传递name参数,给这个线程起名字,对于线程进行区分:

7.1线程的名字

public class Test {public static void main(String[] args) {//String name;Thread t = new Thread(() -> {while(true){System.out.println("hello thread!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}},"这个是新的线程" );//上面的这个就是相当于是在原来的这个表达式的后面多了这个name属性,这样我们使用这个jconsole工具			进行查看的时候,我们就可以看到自己对于这个线程的命名了;t.start();}
}

7.2设置为前台线程

还是上面的这个程序,其实在这个默认的情况下,这个线程就是前台线程,就是我们的这个线程不结束,这个程序就不会结束,但是我们的后台线程就是这个线程结束与否,对于我们的整个程序的结束没有影响;

这个默认情况下是前台线程,我们可以使用这个setDaemon方法把这个线程设置为后台的线程;

我们的这个具体的差别可以在打印的时候看出来,就是我们的这个内容好像没有进行打印,这个程序就结束了;

public class Test {public static void main(String[] args) {//String name;Thread t = new Thread(() -> {while(true){System.out.println("hello thread!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}},"这个是新的线程" );//设置为后台的线程,运行起来就不会进行打印t.setDaemon(true);t.start();}
}

7.3判断线程是否存活

这个就是使用的t.isAlive方法判断这个线程是不是存活的,下面的这个使用start开始的时候,就是存活的线程,打印输出的结果就是true,但是休眠之后,这个就是死亡的,打印输出false;

public class Test {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(()->{System.out.println("线程开始");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程结束");});t.start();System.out.println(t.isAlive());Thread.sleep(2000);System.out.println(t.isAlive());}
}

8.创建线程方法总结

image-20241021125053812

9.start方法

start和run方法的区别:

我们的start方法会去调用这个系统里面的api,进行线程的创建;

run只会去执行这个线程里面的内容(会在这个start创建线程之后自动被调用);

两个的本质区别就是是不是调用了这个系统里面的api,创建出来了新的线程;

10.终止(中断)线程-interrupt

10.1自定义标志位

需要注意的就是这个自定义的静态的变量需要是全局的,不可以是局部的,因为这个lambda是变量的捕捉,这个变量需要是这个final或者是视为这个final的变量;

我们的这个变量一旦写作这个局部的,这个时候我们下面不可以进行修改了,但是我们的有修改,所以写成局部的就会报错,大家可以自己去尝试一下,看看这个效果,这个主要是lambda的语法导致的;

image-20241021132639605

10.2使用系统里面的标志位

为什么会有下面的这个,因为上面的存在缺陷,就是我们需要手动创建,而且我们的线程如果正在休眠,我们把这个标志位修改掉,这个线程无法及时的做出回应,或者是叫做响应;

但是如果我们使用这个系统里面的标志位,这个就是currentThread().isInterrupted()方法进行判断当前的这个线程是不是被打断了;

我们的这个里面有一个t.sleep()这个方法就是进行线程的唤醒,上面的这个手动创建标志位的方法,如果我们的这个线程在休眠,这个时候不可以及时的进行响应,但是我们的这个interrupt这个方法,就会触发这个线程的异常,让这个休眠的线程提前被唤醒,但是我们在运行的时候会发现这个会抛出异常之后,还是会继续执行的;

这个主要原因就是我们的interrupt唤醒这个线程之后,我们的异常抛出把这个设置的标志位清除掉了,就是我们的自动设置的标志位没有了效果,我们的异常让这个标志位的效果失效了,接下来,线程可以自己决定接下来如何进行处理;

image-20241021174301710

11.线程等待join

下面的这个程序就是我们的t线程会休眠,我们的t.join就会让这个主线程等待我们的t线程,直到我们的t线程执行结束,主线程才会结束;

image-20241021181517994

12.线程方法总结

image-20241021183414460
动设置的标志位没有了效果,我们的异常让这个标志位的效果失效了,接下来,线程可以自己决定接下来如何进行处理;

在这里插入图片描述

11.线程等待join

下面的这个程序就是我们的t线程会休眠,我们的t.join就会让这个主线程等待我们的t线程,直到我们的t线程执行结束,主线程才会结束;

在这里插入图片描述

12.线程方法总结

在这里插入图片描述

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

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

相关文章

VAE中的“变分”什么

写在前面 VAE(Variational Autoencoder),中文译为变分自编码器。其中AE(Autoencoder)很好理解。那“变分”指的是什么呢?—其实是“变分推断”。变分推断主要用在VAE的损失函数中,那变分推断是什么&#x…

P11228 [CSP-J 2024] 地图探险(民间数据)

原题链接​​​​​​ 这道题挺简单的&#xff0c;就是把题中所给的条件换成代码就行了&#xff0c;题解感觉不知道咋写&#xff0c;注释都放在代码里了&#xff0c;非常清楚。 //注释非常清楚 #include<bits/stdc.h> using namespace std; const int N 1e3 10; //字…

MobileNetV2实现实时口罩检测tensorflow

项目源码获取方式见文章末尾&#xff01; 回复暗号&#xff1a;13&#xff0c;免费获取600多个深度学习项目资料&#xff0c;快来加入社群一起学习吧。 **《------往期经典推荐------》**项目名称 1.【Informer模型复现项目实战】 2.【卫星图像道路检测DeepLabV3Plus模型】 3.【…

著名AI人工智能的未来应用讲师培训师唐兴通数字经济大数据工业4.0数字化转型AIGC大模型培训讲师

《大数据与人工智能的未来应用》培训课程大纲 一、培训内容简介 本课程旨在帮助学员深度理解大数据与人工智能&#xff08;AI&#xff09;如何为未来商业和行业带来革命性变革。课程紧贴前沿科技&#xff0c;从数据采集、分析到AI应用开发&#xff0c;全方位解析大数据和AI如…

docker环境装gitlab遇到的几个问题

1.运行起gitlab容器后总是间歇性的报出502、500等错误页面&#xff0c;&#xff0c;根本无法正常使用 解决&#xff1a;经过查看日志发现总是报writing value to /dev/shm/gitlab/puma/gauge_all_puma_14-0.db failed with unmapped file"这个错误&#xff0c;去容器中df…

深入浅出:ProcessPoolExecutor 入门指南

深入浅出&#xff1a;ProcessPoolExecutor 入门指南 什么是 ProcessPoolExecutor&#xff1f;为什么要使用 ProcessPoolExecutor&#xff1f;ProcessPoolExecutor 的基本用法1. 导入模块2. 创建进程池3. 提交任务4. 获取任务结果5. 示例代码代码解析运行结果 注意事项总结 在现…

51c~目标检测~合集2

我自己的原文哦~ https://blog.51cto.com/whaosoft/12377509 一、总结 这里概述了基于深度学习的目标检测器的最新发展。同时&#xff0c;还提供了目标检测任务的基准数据集和评估指标的简要概述&#xff0c;以及在识别任务中使用的一些高性能基础架构&#xff0c;其还涵盖了…

从本地到云端:跨用户请求问题的完美解决方案

对于某些单个请求或响应中含有多个用户信息的服务&#xff0c;SDK提供了一套基于统一的UCS拆分和聚合的解决方案供开发者使用。 请求拆分 对于跨用户服务的请求&#xff0c;我们提供了两个处理方案&#xff1a; 【1】根据用户信息拆分请求&#xff1a; 场景&#xff1a;请求内…

Docker | images镜像的常用命令总结

命令总结 1. 帮助启动类命令基本命令systemctl status dockerdocker infodocker --help 2. 镜像命令docker images删除镜像出现错误 docker searchdocker pull xxx[:TAG]docker images -adocker images -qdocker system dfdocker rmi -f xxxxxdocker rmi -f $(docker images -q…

推荐系统中的AB测试

在现代互联网平台中&#xff0c;推荐系统起着至关重要的作用&#xff0c;无论是视频平台、社交网络还是电商网站&#xff0c;推荐系统都能够帮助用户找到最感兴趣的内容。为了不断优化推荐效果&#xff0c;AB测试&#xff08;A/B Testing&#xff09;作为评估新算法或功能改进的…

go语言多态性(接口interface)的使用

前言 在Go语言中&#xff0c;接口类型&#xff08;interface&#xff09;完全可以作为一个函数的参数。这是Go语言多态性的一个重要体现&#xff0c;允许函数接受任何实现了接口中定义的方法的类型的实例。 一、接口&#xff08;interface&#xff09;定义 type Reader inte…

Qt 学习第十四天:线程与多线程

1024程序员快乐&#xff0c;如果这博客让你学习到了知识&#xff0c;请给我一个免费的赞❤️ 父子线程演示 一、创建界面文件 LCDnumber 二、创建mythread类&#xff0c;继承QObject 三、在MyThread.h文件做修改&#xff0c;并且加上函数声明 引入头文件&#xff0c;改变继…

实战:大数据冷热分析

实战&#xff1a;大数据冷热分析 冷热分析&#xff08;Hot and Cold Data Analysis&#xff09;的目的主要在于优化存储系统的性能和成本。通过识别并区分访问频率和存储需求不同的数据&#xff0c;可以采取适当的存储策略&#xff0c;进而提高系统的效率和用户体验。终极目的…

javaScript整数反转

function _reverse(number) { // 补全代码 return (number ).split().reverse().join(); } number &#xff1a;首先&#xff0c;将数字 number 转换为字符串。在 JavaScript 中&#xff0c;当你将一个数字与一个字符串相加时&#xff0c;JavaScript 会自动将数字转换为字符串…

PyTorch中如何进行向量微分、矩阵微分、计算雅各比行列式

文章目录 摘要Abstract 一、计算雅各比行列式二、向量微分三、矩阵微分总结 摘要 本文介绍了在PyTorch中进行向量微分、矩阵微分以及计算雅各比行列式的方法。通过对自动微分&#xff08;Autograd&#xff09;功能的讲解&#xff0c;展示了如何轻松实现复杂的数学运算&#xf…

【MySQL】运维篇—MySQL安装与配置:MySQL的安装与初始配置

安装和配置MySQL是数据库运维的基础&#xff0c;正确的安装和配置可以确保系统的稳定性和安全性。 在本节中&#xff0c;将详细介绍如何在不同平台上安装和配置MySQL&#xff0c;包括Windows、Linux&#xff08;Ubuntu&#xff09;和macOS。每个示例都将包括详细的步骤和代码注…

unity3d——PlayerPrefs day01——基础知识点

Unity3D中的PlayerPrefs是一个用于存储和读取玩家数据的公共类&#xff0c;它提供了一种简单、轻量级的数据存储解决方案。以下是关于PlayerPrefs的所有知识点&#xff1a; 一、基本概念与工作原理 定义&#xff1a;PlayerPrefs是Unity3D提供的一种本地持久化数据存储方式&am…

代码编辑组件

代码编辑组件 文章说明核心代码运行演示源码下载 文章说明 拖了很久&#xff0c;总算是自己写了一个简单的代码编辑组件&#xff0c;虽然还有不少的bug&#xff0c;真的很难写&#xff0c;在写的过程中感觉自己的前端技术根本不够用&#xff0c;好像总是方案不够好&#xff1b;…

Java 集合交集判断

Java 集合交集判断 一. 使用 retainAll()方法二. 使用 removeAll() 方法与判断集合大小三. 使用 Stream 流式处理四. 使用 Collections.disjoint() 方法五. 总结六. 参考文章 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续…

Flux 开源替代,他来了——Liberflux

LibreFLUX 是 FLUX.1-schnell 的 Apache 2.0 版本&#xff0c;它提供完整的 T5 上下文长度&#xff0c;使用注意力屏蔽&#xff0c;恢复了无分类器引导&#xff0c;并完全删除了 FLUX 美学微调/DPO 的大部分内容。 这意味着它比基本通量要难看得多&#xff0c;但它有可能更容易…