多线程 --- 多线程编程

在写代码的时候,可以使用多进程进行并发编程(在Java中,不太推荐,很多很多关于进程相关的API,在Java标准库中,都没有提供),也可以使用多线程进行并发编程(系统提供了多线程编程的API,Java标准库,已经将这些API封装了,在代码中可以直接使用)。

目录

创建线程

方法1 继承Thread类

方法2: 实现Runnable接口,重写run方法

方法3. 继承Thread,重写run,但是使用匿名内部类

方法4. 实现Runnable,重写 run,匿名内部类

方法5. 使用 lambda 表达式(常用)

完!


创建线程

方法1 继承Thread类

继承Thread来创建一个线程类

细节补充:

        1. 第一行代码: class MyThread extends Thread 中,继承的Thread这个类,好像可以直接使用,并不像Scanner,ArrayList,Random一样需要导包,这是为什么呢?

        答:Java标准库中,有一个特殊的包,java.lang包,这个包中包含了Thread类,且这个lang包是Java自动引入的。

        2.自己创建的类MyThread中,类前面没有public,这是怎么回事?前面能不能写public?

        答:一个 .java文件中,只能有一个public的类。 一个类前面如果没有范围限定符,那就默认是包级作用域,就是只能在当前包中被其他类使用。

        3.怎么理解run方法是线程的入口方法?

        答:此处的run方法,并不需要程序员来手动的进行调用,该方法会在线程创建好之后,一个合适的时机,被JVM自动的调用执行。就类似于,main方法,是一个java进程的入口方法。一个进程中,至少会有一个线程,这个进程中的第一个线程,也就称为”主线程“。main方法,也就是主线程的入口方法。

补充:这种风格的函数,称之为”回调函数“(callback)

在Java数据结构中,优先级队列PriorityQueue,在使用的时候,必须要给对象实现一个比较的接口,才能进行使用。

回调函数 :作为参数传递给另一个函数,在该函数内部的某个特定时刻被调用执行的函数。其核心特点是函数的调用时机由其他代码控制run方法的函数的调用时机由其他代码(这里是 JVM)控制

        4.run方法上面的 @Override 是什么意思?

        答:方法重写。本质上:是让程序员能够对现有的类,进行扩展。我们要搞一个线程,肯定是要让这个线程执行一些代码的,Thread类本身会带有一个run方法。但很明显,标准库自带的run方法,是不知道我们的具体需求的,业务要求是什么样?必须要手动的进行指定,Override就是针对原有的Thread进行扩展(把一些能够复用的,进行了重用,把需要扩展的,进行扩展)。Thread这个API中,会有很多的属性方法,大部分内容直接复用即可,把需要扩展的内容,进行扩展即可。

        那如果没有@Override这个注解,貌似也可以实现方法重写呀。为啥还要写这个注解呢?语法中有很多的机制,是方便让编译器,对我们的代码进行自动检查的。(人是非常不靠谱的!!!机器是较为靠谱的!!!)(就比如fina限定符,限定一个变量不能再被修改,就是方便让编译器为我们进行自动检查的)

        5.一般来说,实例化的时候都是方式2。

         6.什么是操作系统的”内核“?

        答:操作系统中,最核心部分的功能模块(管理硬件,给软件提供稳定的运行环境)

        操作系统大致可以分为内核空间(内核态)和用户空间(用户态),平时运行的普通的应用程序,网易云音乐,idea,qq等等,都是运行在用户态的。但是,这些应用程序,有时候,需要针对一些系统提供的硬件资源来进行操作。这些操作,都不是应用程序直接操作的,就是需要调用操作系统提供的API,进一步在内核中完成这样的操作

        为啥还要分出用户态和内核态?

        最主要的目的还是稳定。防止某些应用程序把硬件设备或者软件资源给搞坏了。系统封装了一些API,这些API都属于是一些”合法“的操作,不会对硬件设备或者软件资源有破坏,应用程序只能调用这些API来实现对应的功能,从而不至于对系统 / 硬件设备产生太大的危害。假如让应用系统直接操作硬件,在极端条件下,代码出现bug,会把硬件干坏。

        (就比如是银行系统,办事窗口里的工作人员,就是正经的经过培训政审的安全人员,不会对银行产生危害,用户要办理各种业务,就需要在办事窗口前,给工作人员说清楚需求,由工作人员代办)

具体解释:每个线程都是一个独立的执行流,每个线程都能够独立的去CPU上调度执行

如下图代码:

下面的死循环,是在 t 线程中执行的

而这个死循环,是在 main 线程中执行的

以之前的理解,如果一个代码中,出现了两个死循环,则肯定最多只能执行一个,另一个循环就进不去了。但我们把进程运行起来,可以看到,两个循环,都在执行!这两个线程,就是两个独立的执行流,也就解释了我们最开始那句话:每个线程都是一个独立的执行流,每个线程都能够独立的去CPU上调度执行

在调用start方法,创建线程之后,兵分两路,一路,沿着main方法,继续执行,打印hello main,另一路,进入到线程的run方法,打印hello thread。是相互独立的,互不干扰的。

注意:

        当有多个线程的时候,这些线程的执行的先后顺序,是不确定的!!!(这一点,是因为操作系统内核中,有一个”调度器“模块,这个模块的实现方法,是一种类似于”随机调度“的效果)

        那随即调度又是什么呢?

1. 一个线程,什么时候调度到CPU上去执行,时机是不确定的。

2. 一个线程,什么时候从 CPU上下来,给别人让位,时机也是不确定的)

也称为”抢占式执行“。

所以,每秒钟到底是先执行 main 还是先执行thread,这是不一定的(随机调度,抢占式执行)

主线程,在调用 strat 方法之后,就立即往下执行打印了,与此同时,内核就要通过刚才 API 构建出线程,并且执行run。由于创建线程本身也有开销(虽然开销比创建进程低,但也不是0).所以,在第一轮打印中,因为创建线程有一定的开销影响,导致hello thread一般情况下 都比hello main略慢一筹。

我们刚刚,只是通过打印的方式,看到了两个执行流,还可以通过一些第三方工具,更直观的来查看多个线程的情况。 --> JDK中,有一个jconsole工具,该工具在JDK的bin文件夹中(该工具,只能列出java的进程,其他不是java的进程,无法进行分析)

注意,我们需要先把进程运行起来,然后再打开工具,找到对应的类名,然后进行链接

进入之后,选择线程

main 对应的就是main方法的主线程

黄色框的,就是我们缩写的代码,创建的 t 线程(这个Thread - 0,1,2...是默认名称 可以改)

剩下的线程,都是JVM自带的线程,这些自带的线程,要完成一些垃圾回收,监控统计各种指标....

点进具体的线程,就可以看到相关的调用栈(线程里当前执行到了那个方法的第几行代码,这个方法是如何如何一层调用过去的...)

这里这个线程的在不断的运行的,点击线程详细情况的这个瞬间,就相当于咔嚓来一个闪照一样,把这一瞬间的状态展示到这里了。

这就是使用 jconsole 来检测线程的方法

再回头看我们的举例代码

我们在循环中加入了sleep方法,来降低这两个死循环的速度(在C语言中,用的是Windows api中提供的Sleep函数,Windows.h),我们此处使用的sleep,是Java中封装后的版本,属于是Thread提供的静态方法。

如果我们不进行 try - catch的话,该方法会编译报错,为什么必须需要抛出异常呢? --> 这个异常,意味着,在sleep(1000)的过程中,可能会被提前唤醒。(换句话说:sleep(1000)的作用是休眠1s,但在这休眠1s中,可能会被其他操作给提前唤醒,也就是没休眠够一秒,就被唤醒了,此处我们就应该在catch块中 提出具体的解决方法,到底应该怎么做)

还有一点需要注意:

为什么在run方法中,解决sleep的异常只能是try - catch方法

但是在main方法中,可以是try - catch方法,也可以是在方法体上throws出异常

为什么呢?

原因很简单,run方法中的sleep,如果加上throws的话,就修改了方法签名了,这样的话是无法构成”重写“的,因为父类的run方法中,并没有throws这个异常,子类重写的时候,也就不能throws异常。

方法2: 实现Runnable接口,重写run方法

1.实现Runnable接口

Runnable可以理解成”可执行的“,通过这个接口,就可以抽象表示出一段可以被其他实例来执行的代码。

 2.创建Thread实例,调用Thread的构造方法时,将Runnable对象作为参数传入。

Runnable接口,还需要搭配Thread类,才能真正在系统中创建出线程

3.调用start方法

上面这种写法,其实是把 线程 和 要执行的任务 进行了解耦合了。

方法3. 继承Thread,重写run,但是使用匿名内部类

匿名内部类:是一种内部类,在一个类里面定义的类,其最大的特点是-->没有名字,不能重复使用,用一次之后就找不到了。

解释:

写这个 { } 的意思是:定义一个类,与此同时,这个新的类,继承自Thread。此处的 { } 中,可以定义子类的属性和方法。此处最主要的目的就是重写 run 方法。与此同时,这个代码,还创建了子类的实例。且 t 指向的实例,并非是单纯的Thread,而是 Thread的子类(但并不知道这个子类叫什么,因为是匿名的内部类)

方法4. 实现Runnable,重写 run,匿名内部类

这种方法,在Thread构造方法的参数中,填写了 Runnable 的匿名内部类的实例

方法5. 使用 lambda 表达式(常用)

在lambda表达式中, () 中是形参列表,这里可以带参数,但因为线程的入口不需要参数,所以这里为空,() 的前面,应该还有一个函数名,此处作为匿名函数,就没有名字了。

解释:

在Java中,方法是不能脱离类单独存在的,所以就不得不设置回调函数,从而多套了一层。但与此同时,java 语法也开了一个特殊的口子,就是 lambda 表达式,函数式接口,属于是 lambda 背后的实现,相当于 java 在没破坏原有的规则的基础上,给了一个 lambda 一个合法性的解释。

上述五种方法,都是等价的,可以互相转换的,只不过是第五种 lambda 表达式的方法,更为简洁一些,更加常用一些!

完!

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

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

相关文章

HTML~视频音频在网页中不能自动播放

问题: autoplay是打开自动播放,但是发现加了关键词还是没有反应 原因: 现在浏览器禁止自动播放(特别是带声音的) 解决办法: 可以添加muted 进行没有声音的自动播放

vue中上传接口file表单提交二进制文件流

1.使用elementui上传组件 要做一个选择文件后&#xff0c;先不上传&#xff0c;等最后点击确定后&#xff0c;把file二进制流及附加参数一起提交上去。 首先使用elementui中的上传组件&#xff0c;设置auto-uploadfalse&#xff0c;也就是选择文件后不立刻上传。 <el-uplo…

Flutter环境配置

配置环境变量 PUB_HOSTED_URLhttps://pub.flutter-io.cnFLUTTER_STORAGE_BASE_URLhttps://storage.flutter-io.cn 这个命令是用来配置 Flutter 的镜像源地址&#xff0c;主要是为了解决在中国大陆地区访问 Flutter 官方资源较慢的问题。 具体的操作如下&#xff1a; 右键点…

ngx_http_index_set_index

定义在 src\http\modules\ngx_http_index_module.c static char * ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {ngx_http_index_loc_conf_t *ilcf conf;ngx_str_t *value;ngx_uint_t i, n;ngx_http_inde…

数据库的视图有什么用?

数据库的视图&#xff08;View&#xff09;是一种虚拟表&#xff0c;它的内容由查询定义&#xff0c;并不实际存储数据&#xff0c;而是动态生成。视图的主要作用如下&#xff1a; 1. 简化复杂查询 场景&#xff1a;当查询涉及多表连接、复杂过滤或聚合操作时&#xff0c;SQL …

Ubuntu 24.04 安装 Docker 详细教程

前言 Docker 是目前最流行的容器化技术&#xff0c;它可以帮助开发者快速部署和运行应用程序。本文将详细介绍在 Ubuntu 24.04 (Noble Numbat) 上安装 Docker 的完整步骤&#xff0c;包括配置镜像加速等实用技巧。 一、准备工作 1.1 系统要求 Ubuntu 24.04 LTS 具有 sudo 权…

鸿蒙进行视频上传,使用 request.uploadFile方法

一.拉起选择器进行视频选择&#xff0c;并且创建文件名称 async getPictureFromAlbum() {// 拉起相册&#xff0c;选择图片let PhotoSelectOptions new photoAccessHelper.PhotoSelectOptions();PhotoSelectOptions.MIMEType photoAccessHelper.PhotoViewMIMETypes.VIDEO_TY…

C++ map容器总结

map基本概念 简介&#xff1a; map中所有元素都是pair pair中第一个元素为key&#xff08;键值&#xff09;&#xff0c;起到索引作用&#xff0c;第二个元素为value&#xff08;实值&#xff09; 所有元素都会根据元素的键值自动排序 本质&#xff1a; map/multimap属于关…

【Zookeeper搭建(跟练版)】Zookeeper分布式集群搭建

&#xff08;一&#xff09;克隆前的准备 1. 用 xftp 发送文件 2. 时间同步&#xff1a; sudo cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 3. zookpeeper 安装 部署 呼应开头发送的压缩包&#xff0c;解压&#xff1a; cd ~ tar -zxvf zookeeper-3.4.6.tar.g…

Flutter项目之页面实现以及路由fluro

目录&#xff1a; 1、项目代码结构2、页面编写以及路由配置main.dart(入口文件)page_content.dartindex.dart&#xff08;首页&#xff09;application.dart&#xff08;启动加载类&#xff09;pubspec.yaml&#xff08;依赖配置文件&#xff09;login.dart&#xff08;登录页&…

记录Jmeter 利用BeanShell 脚本解析JSON字符串

下载org.json包(文档说明) #下载地址 https://www.json.org/ # github 地址 https://github.com/stleary/JSON-java # api 文档说明 https://resources.arcgis.com/en/help/arcobjects-java/api/arcobjects/com/esri/arcgis/server/json/JSONObject.htmlBeanShell脚本 import…

uniapp动态循环表单校验失败:初始值校验

问题现象 &#x1f4a5; 在实现动态增减的单价输入表单时&#xff08;基于uv-form组件&#xff09;&#xff0c;遇到以下诡异现象&#xff1a; <uv-input>的v-model绑定初始值为数字类型时&#xff0c;required规则失效 ❌数字类型与字符串类型校验表现不一致 &#x1…

UML 图六种箭头含义详解:泛化、实现、依赖、关联、聚合、组合

目录 一、泛化&#xff08;Generalization&#xff09; 概念 表示方法 二、实现&#xff08;Realization&#xff09; 概念 表示方法 三、依赖&#xff08;Dependency&#xff09; 概念 表示方法 四、关联&#xff08;Association&#xff09; 概念 表示方法 五、…

Android Logcat总结

文章目录 Android Logcat总结日志格式过滤日志正向过滤反向过滤正则过滤日志等级 Android Logcat总结 日志格式 用法&#xff1a; Log.e("TAG", "hello") Log.i("TAG", "hello") Log.d("TAG", "hello")依次为&…

Unity UGUI - 六大基础组件

目录 一、Canvas上 1. Canvas&#xff1a;复制渲染子UI控件 2. ✨Canvas Scaler✨&#xff1a;画布分辨率自适应 3. Graphics Raycaster&#xff1a;射线事件响应 4. ✨Rect Transform✨&#xff1a;UI位置锚点对齐 二、Event System上 5. Event System 6. Standalone …

基于Springboot的网上订餐系统 【源码】+【PPT】+【开题报告】+【论文】

网上订餐系统是一个基于Java语言和Spring Boot框架开发的Web应用&#xff0c;旨在为用户和管理员提供一个便捷的订餐平台。该系统通过简化餐饮订购和管理流程&#xff0c;为用户提供快速、高效的在线订餐体验&#xff0c;同时也为管理员提供完善的后台管理功能&#xff0c;帮助…

css 实现闪烁光标

要实现闪烁光标&#xff08;比如文本输入框内常见的闪烁效果&#xff09;&#xff0c;可以使用 CSS 动画。下面是一个简单的方法&#xff1a; 代码示例 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta n…

从JVM底层揭开Java方法重载与重写的面纱:原理、区别与高频面试题突破

&#x1f31f;引言&#xff1a;一场由方法调用引发的"血案" 2018年&#xff0c;某电商平台在"双十一"大促期间遭遇严重系统故障。 技术团队排查发现&#xff0c;问题根源竟是一个继承体系中的方法重写未被正确处理&#xff0c;导致订单金额计算出现指数级…

详解Spark executor

在 Apache Spark 中&#xff0c;Executor&#xff08;执行器&#xff09; 是运行在集群工作节点&#xff08;Worker Node&#xff09;上的进程&#xff0c;负责执行具体的计算任务并管理数据。它是 Spark 分布式计算的核心组件之一&#xff0c;直接决定了任务的并行度和资源利用…

适配器模式及其典型应用

引言 适配器模式&#xff08;Adapter Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许不兼容的接口协同工作。适配器模式通过创建一个适配器类来转换一个类的接口&#xff0c;使其能够与另一个类的接口兼容。这种模式在实际开发中非常有用&#xff0c;特别是在需要…