公司网站建设的视频教程/网站推广上首页
个人主页:兜里有颗棉花糖
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创
收录于专栏【Java系列专栏】【JaveEE学习专栏】
本专栏旨在分享学习Java的一点学习心得,欢迎大家在评论区交流讨论💌
目录
- 一、前文回顾
- 二、创建线程的几种方式。
- 继承Thread类
- 实现runnable方法
- lambda表达式
- 三、Thread类及常见方法
- Thread类的常见构造方法
- Thread类的属性
一、前文回顾
我们先来回顾一下线程与进程之间的联系。
我们知道多进程可以帮助我们完成并发编程,即可以把多个cpu核心充分利用起来以完成同时执行多任务的场景。但是进程有一个问题就是进程的创建和销毁的开销是比较大的,如果我们需要频繁的创建和销毁进程的话,那么多进程这种方式就是比较低效的。所以就衍生出了轻量级进程——线程。线程之所以比进程更轻量级主要有两方面的原因:
- 一方面是线程共享进程的资源,不要忘记,线程是进程的一部分,线程可以共享那些属于进程的资源,但是进程之间是相对独立的,线程创建时不需要像进程那样需要分配独立的资源,
因此线程创建和销毁的开销会比进程小很多
; - 另一方面
线程调度切换要比进程调度切换要快上很多
,由于多线程使用的调度算法比单进程要复杂很多,加上线程对系统资源的共享(线程在同一个进程内共享资源,线程切换只需要切换线程的上下文,而不需要切换整个进程的上下文;而相比之间进程切换需要保存和恢复进程的所有上下文信息,包括内存映像、打开的文件、进程状态等。),所以线程调度之间的切换要比进程调度的切换快上很多,也更容易实现多任务的处理。
线程在同一进程内共享内存资源,所以当进程创建完成时,线程已经可以访问和共享这些内存资源(主线程是在进程创建时默认创建的一个线程)。如果后续需要我们创建其它的线程,我们就可以重用之前的字眼就可以了。
进程包含了线程,一个进程内部至少有一个线程,进程是系统中分配资源的基本单位,而线程是cpu调度执行的基本单位。我们可以这样说线程的引入最主要的就是为了实现更高效的并发处理和资源共享。
由于线程可以共享同一进程内的内存资源,因此资源分配的主要任务是为进程分配足够的内存空间,对线程的资源分配比在进程级别上的资源分配要少得多。
现在问题来了,一个进程内既然可以存在多个线程,同时线程可以共享同一个进程内的资源,这也意味着这多个线程之间的相互干扰会比较大(这一点就不如进程之间那样相对独立一些,一个进程挂掉知道后不会对其它进程产生影响。),一旦某个出现异常之后就有可能导致整个进程都会挂掉,这当然会对其它线程产生影响。
另外,多个线程去访问同一块公共资源的时候也可能会出现冲突带来线程安全的问题。
所以,总的来说使用多线程的方式完成任务比多进程的方式完成任务会有优势,但同时存在一些缺点。但尽管如此,我们依然可以利用多线程的方式来很好的完成并发编程的任务。
好了,友友们,回顾到此结束,接下来我们一起学习新的内容吧!!!
二、创建线程的几种方式。
Java标准库中,提供了Thread()类
来表示线程,同时Java创建线程的方式有很多种。最常见的两种写法:继承Thread()类
、重写Runnable方法()
。
继承Thread类
方式一
:通过继承thread类来重写run方法是一种创建线程的方式(这里我们需要自己创建一个类来继承thread方法);方式二
:还有一种方式我们也可以创建线程:基于匿名内部类的形式来继承thread类,并重run方法。
现在我们通过匿名内部类的方式来创建线程(基于匿名内部类的形式来继承thread类,并重写run方法,即方式二):
代码如下:
public class Demo03 {public static void main(String[] args) {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);}}}};t.start();}
}
解释:上述代码中我们创建了一个子类,这个子类(此类就是我们说的匿名内部类)继承自Thread类,同时我们在这个子类重对run方法进行了重写。
另外我们还创建了该子类(也就是之前说的匿名内部类)的实例,用引用t
类指向这个实例。
最后就是通过引用t来调用start方法来调用系统API,再从内核中把线程创建出来。
最后我们来看一下运行结果:
实现runnable方法
方式三
:通过实现runnable
来重写run方法(这里是自己创建一个类)的方式可以创建一个线程。方式四
:另外我们这里依然可以用其它方式来创建一个线程:即基于匿名内部类的形式来实现runnable并重写run方法。
现在我们通过匿名内部类的形式来实现runnable并重写run方法(即方式四)来创建线程,代码如下:
public class Demo04 {public static void main(String[] args) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {while(true) {System.out.println("hello world");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}});t.start();}
}
解释,上述代码中,我们创建了
runnable
的子类,并重写了run方法
,然后创建出了runnable的子类的实例,把这个实例传给Thread的构造方法。
lambda表达式
我们还可以通过lambda表达式来表示run方法的内容,从而创建出线程。
代码如下:
public class Demo05 {public static void main(String[] args) {Thread t = new Thread(() -> {while(true) {System.out.println("hello world");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();}
}
什么是lambda表达式:lambda表达式本质上就是一个匿名函数,主要可以用来作为回调函数来进行使用。
先来回顾一下回调函数吧:回调函数不需要我们自己去进行调用,因为回调函数会在特定的时机自动地被调用。
此时的回调函数就是在线程创建成功之后才会真正执行。
关于线程的创建这里,也有其它的方式,比如基于callable的方式创建线程,基于线程池的方式创建线程。
三、Thread类及常见方法
Thread类是JVM
用来管理线程的一个类,换句话说,每个线程都有一个唯一的Thread对象与之关联。我们对线程的各种操作都是根据Thread类进行展开的。
Thread类的常见构造方法
这里我们可以参照Java官方文档:https://docs.oracle.com/javase/8/docs/api/index.html
如下图的代码中,我们给线程起了个名字
mythread
我们可以通过JVM
中的jconsole.exe
来看到这个线程(mythread
):
现在问题来了,为什么我们没有看到主线程(main)呢?在上述代码中,主线程创建并启动了一个新线程,而新线程负责打印"hello thread"
的消息。由于新线程的循环中有一个线程休眠的操作(Thread.sleep(1000)),所以您将会在不同时间间隔内看到"hello thread"的输出。但这个输出是由新线程负责的,而不是主线程。
所以,我们没有看到主线程的原因是主线程没有显示任何输出或执行其他代码来表明它的存在。它仅仅完成了创建和启动新线程的任务,然后退出。
对于主线程来说,main方法
就是主线程的入口;而对于其它线程来说,lambda或者run就是线程的入口。所以执行完线程的入口函数,线程就算是结束了。
Thread类的属性
下面是Thread类的几个常见属性:
属性 | 获取方法 |
---|---|
ID | getId() |
名称 | getName() |
状态 | getState() |
优先级 | getPriority() |
是否后台线程 | isDasmon() |
是否存活 | isAlive() |
是否被中断 | isInterrupted() |
- ID:这里是JVM给线程设定的身份标识。对于一个线程来说,身份标识可以有好多个,就像一个人有大名也有小名。比如JVM给某一线程设定了一个身份标识,phread库(系统给开发人员提供的操作线程的API)也有一个线程的身份标识,内核中还有一个线程的身份标识,这几个身份标识之间相互独立互不干扰。
- 名称:设置线程名称方便我们知道这个线程是干什么用的,同时也方便我们去进行调试。
- 状态:Java中的线程状态和操作系统中的有所区别,Java中的线程状态更加细化。
- 优先级:关于获取、设置线程的优先级其实并没有太大意义,因为
系统内核进行线程调度的速度极快(线程调度是由系统内核负责的)
,快到我们根本无法感知,所有我们一般使用默认的线程优先级即可。 - 是否是后台线程:后台线程又称为守护线程,后台线程不会影响线程结束;而前台线程会影响线程结束,如果前台线程没有执行完的话,进程是不会结束的;一个进程中如果所有的前台线程都执行完此时进程退出,如果此时依然存在后台线程没有执行完的话,后台线程依然会随着进程的退出而退出。
我们创建的线程默认是前台线程。
如下代码进行演示:
// 这里我们创建的线程是前台线程
public class Demo07 {public static void main(String[] args) {Thread t = new Thread(()->{while(true) {System.out.println("hello thread!!!");}});t.start();}
}// 这里将我们创建的线程设置为了后台线程
public class Demo07 {public static void main(String[] args) {Thread t = new Thread(()->{while(true) {System.out.println("hello thread!!!");}});t.setDaemon(true);t.start();}
}
当我们把创建的线程设置为后台线程之后,程序运行起来之后就会立即结束。原因:由于我们把自己手动的线程创建成了后台线程,所以此时就只剩下main主线程了,而main线程这里不需要执行代码,所以执行时间极短main线程(前台线程)就结束了,而我们设置的后台线程还没有来得及执行就随着进程的退出而退出了。
- 是否存活(
isAlive()
):这里的存活指的并不是thread对象是否存活,而是指的thread对象(我们也可以称为线程对象)对应的线程(即系统内核中的线程)是否存活。Thread对象的声明周期并不是和系统内核中的线程的生命周期完全一致。一般来说都是先把thread对象创建好,然后手动调用start方法,此时内核才真正创建出线程。
对于线程,当它的run方法执行完后,线程的生命周期也会自然结束,就算线程对象还存在于内存中。此时该线程会释放占用的资源并进入死亡状态;还有另外一种情况就是线程对象生命周期的结束(即没有引用指向该线程对象了),此时线程也会结束,这种情况下,线程的run方法执行与否并不影响线程的结束状态。垃圾回收器会在适当时候回收无引用的线程对象,并释放相关资源。
好了,本文到这里就结束了,希望友友们可以支持一下一键三连哈。嗯,就到这里吧,再见啦!!!
相关文章:

【Java系列】详解多线程(二)——Thread类及常见方法(上篇)
个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【Java系列专栏】【JaveEE学习专栏】 本专栏旨在分享学习Java的一点学习心得,欢迎大家在评论区交流讨论💌 目录 一…...

Android Dialog 弹出时,隐藏 navigation bar
1、概述 一些场合,要求界面是全屏的,然而在全屏界面下,弹出dialog 又会导致虚拟按键栏重新弹出来,也是比较难受的。(而且查了非常多方法都是不能完美的解决这个问题,要么是压根不能用,要么是会闪一下虚拟栏…...

LeetCode(Hot100)——1:两数之和
方法1:暴力求解 利用两次for循环来处理, 外循环确定一个数字, 利用内循环不断求和来判断是否两数之和为target,来进行求解。 public class LeetCode1 {Test//测试方法public void test() {int [] nums{2,7,11,15};int target9;/…...

【Qt】报错error:undefined reference to `vtable for Consumer‘的解决方法
1. 问题原因 在创建完程序后,点击构建,显示编译错误。 错误问题如下: error: undefined reference to vtable在编译输出中查看显示如下: error:undefined reference to vtable for custom2. 原因分析 这个错误通常是因为 C 的虚函数表&am…...

【linux系统】用户功能与权限详细总结
前言 菜某的笔记总结,有错误还请指正。 linux用户的概念与root用户 这么理解:一台电脑有多个操作者,每个操作者只能无限制操作自己文件夹中的东西,其他地方的操作需要给与相应权限才能操作。 root用户:就是最高级的…...

ELK简单介绍二
学习目标 能够部署kibana并连接elasticsearch集群能够通过kibana查看elasticsearch索引信息知道用filebeat收集日志相对于logstash的优点能够安装filebeat能够使用filebeat收集日志并传输给logstash kibana kibana介绍 Kibana是一个开源的可视化平台,可以为ElasticSearch集群…...

video 标签 各种属性及所有事件监听
网页中的video 属性和事件,用于计算观看视频的时长,其他用法备存。 <!-- video 不支持 IE8及以下版本浏览器,支持三种视频格式:MP4,WebM 和 Ogg --><video src"test.mp4" controls width"400…...

TS中断言、转换的应用
1.TS 类型断言定义 把两种能有重叠关系的数据类型进行相互转换的一种 TS 语法,把其中的一种数据类型转换成另外一种数据类型。类型断言和类型转换产生的效果一样,但语法格式不同。 2.TS 类型断言语法格式 A 数据类型的变量 as B 数据类型 。 A 数据类…...

【代码随想录算法训练营-第四天】【链表】24,19, 面试题 02.07,142
24. 两两交换链表中的节点 第一遍-递归-小看了一下题解 思路: 读了两遍题目才理解…相邻节点的交换,这个操作很容易实现,但需要一个tmpNode因为是链表的题目,没开始思考之前先加了dummyNode,还真管用把dummyNode作为…...

代理设计模式
1. 代理模式 1.1 代理模式的原理分析 代理设计模式(Proxy Design Pattern)是一种结构型设计模式,它为其他对象提供一个代理对象,以控制对这个对象的访问。代理模式可以用于实现懒加载、安全访问控制、日志记录等功能。 代理模式…...

ubuntu安装docker及docker常用命令
docker里有三个部分 daemon 镜像 和 容器 我们需要了解的概念 容器 镜像 数据卷 文章目录 docker命令docker镜像相关命令docker容器相关命令数据卷ubuntu安装docker docker命令 #启动,停止,重启docker systemctl start docker systemctl stop docker s…...

STM32-TIM定时器输出比较
目录 一、输出比较简介 二、PWM简介 三、输出比较通道(通用) 四、输出比较通道(高级) 五、输出比较模式 六、PWM基本结构 七、PWM参数计算 八、外设介绍 8.1 舵机 8.2 直流电机及驱动 九、开发步骤 十、输出比较库函数…...

《Easy3d+Qt+VTK》学习
《Easy3dQtVTK》学习-1、编译与配置 一、编译二、配置注 一、编译 1、 资源下载:easy3d giuhub 2、解压缩 3、用qt打开CMakeLists.txt即可 4、点击项目,选择debug或者release,图中3处可自行选择,因为我的qt版本是6,…...

多平台展示预约的服装小程序效果如何
线下实体服装店非常多,主要以同城生意为主,但随着电商经济增长,传统线下自然流量变少,商家们会选择线上入驻平台开店获得更多线上用户,包括自建私域小程序等。 而除了直接卖货外,线上展示预约在服装行业也…...

Gti GUI添加标签
通过Git Gui打开项目,通过菜单打开分支历史,我这里是名为"develop"的分支 选中需要打标签的commit,右键-Create tag即可 但貌似无法删除标签,只能通过git bash,本地标签通过git tag -d tagname,…...

高云GW1NSR-4C开发板M3硬核应用
1.M3硬核IP下载:Embedded M3 Hard Core in GW1NS-4C - 科技 - 广东高云半导体科技股份有限公司 (gowinsemi.com.cn) 特别说明:IDE必须是1.9.9及以后版本,1.9.8会导致编译失败(1.9.8下1.1.3版本IP核可用) 以下根据官方…...

【RTOS学习】模拟实现任务切换 | 寄存器和栈的变化
🐱作者:一只大喵咪1201 🐱专栏:《RTOS学习》 🔥格言:你只管努力,剩下的交给时间! 目录 🏀认识任务切换🏐切换的实质🏐栈中的内容🏐切…...

1.2 轻量级数据交互格式–JSON
对于接口来说,数据交互大部分都是使用的JSON格式,我们这里说的数据,就是我们上一章里讲解HTTP协议的时候,HTTP协议结构里的实体,也就是放在body里。body里存放需要传输的数据,数据是JSON格式,然后通过HTTP协议来传输给接口,接口再以同样的方式给我们返回。理解了这一层…...

charCodeAt() 方法
charCodeAt() 是 JavaScript 中用于获取字符串指定位置字符的 Unicode 编码的方法 语法如下: str.charCodeAt(index) str:要获取字符的字符串。index:要获取的字符在字符串中的索引。返回值是一个表示给定索引处字符 Unicode 编码的整数。…...

Flask中redis的配置与使用
注意点: 在__init__.py中需要将redis_store设置成全局变量,这样方便其他文件导入 一、config.py import logging import os from datetime import timedeltafrom redis import StrictRedisclass Config:# 调试信息DEBUG TrueSECRET_KEY os.urandom(3…...

生产者与消费者模型
初识linux之线程同步与生产者消费者模型_生产者线程和消费者线程-CSDN博客 Linux线程(三)—— 多线程(生产者消费者模型、信号量、线程池)-CSDN博客...

透析回溯的模板
关卡名 认识回溯思想 我会了✔️ 内容 1.复习递归和N叉树,理解相关代码是如何实现的 ✔️ 2.理解回溯到底怎么回事 ✔️ 3.掌握如何使用回溯来解决二叉树的路径问题 ✔️ 回溯可以视为递归的拓展,很多思想和解法都与递归密切相关,在很多…...

浅谈web性能测试
什么是性能测试? web性能应该注意些什么? 性能测试,简而言之就是模仿用户对一个系统进行大批量的操作,得出系统各项性能指标和性能瓶颈,并从中发现存在的问题,通过多方协助调优的过程。而web端的性能测试…...

Qt 容器QGroupBox带有标题的组框框架
控件简介 QGroupBox 小部件提供一个带有标题的组框框架。一般与一组或者是同类型的部件一起使用。教你会用,怎么用的强大就靠你了靓仔、靓妹。 用法示例 例 qgroupbox,组框示例(难度:简单),使用 3 个 QRadioButton 单选框按钮,与QVBoxLayout(垂直布局)来展示组框的…...

Linux系统解决“Key was rejected by service”
Linux系统下加载驱动模块出现如上错误提示的原因为:此驱动未经过签名。 方法一、关闭Secure Boot 如果是物理机,需要开机进入BIOS,找到“Secure Boot”的选项,然后关闭。 如果是虚拟机,可以打开虚拟设置,…...

【C++ Primer Plus学习记录】字符函数库cctype
C从C语言继承了一个与字符相关的、非常方便的函数软件包,它可以简化诸如确定字符是否为大写字母、数字、标点符号等工作,这些函数的原型是在头文件cctype中定义的。 cctype中的字符函数 函数名称返回值isalnum()如果参数是字母或数字,该函数返…...

C# WebSocket简单使用
文章目录 前言Fleck调试工具初始化简单使用 前言 最近接到了一个需求,需要网页实现上位机的功能。那就对数据传输的实时性要求很高。那就只能用WebSocket了。这里简单说一下我的WebSocket如何搭建 Fleck C# WebSocket(Fleck) 客户端:html Winfrom Fleck Github官网…...

uni-app 一些实用的页面模板
时间倒计时 <!-- 时间倒计时 --> <template><view class"container"><view class"flex-row time-box"><view class"time-item">{{ laveTimeList[0] }}</view><text>天</text><view class&qu…...

STM32——震动传感器点亮LED灯
震动传感器简单介绍 若产品不震动,模块上的 DO 口输出高电平; 若产品震动,模块上的 DO 口输出低电平,D0-LED绿色指示灯亮。 震动传感器与STM32的接线 编程实现 需求:当震动传感器接收到震动信号时,使用中断…...

使用 Timm 库替换 YOLOv8 主干网络 | 1000+ 主干融合YOLOv8
文章目录 前言版本差异说明替换方法parse_moedl( ) 方法_predict_once( ) 方法修改 yaml ,加载主干论文引用timm 是一个包含最先进计算机视觉模型、层、工具、优化器、调度器、数据加载器、数据增强和训练/评估脚本的库。 该库内置了 700 多个预训练模型,并且设计灵活易用。…...