当前位置: 首页 > news >正文

【JavaEE】Java中的多线程 (Thread类)

作者主页:paper jie_博客

本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。

本文录入于《JavaEE》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力)打造,将MySQL基础知识一网打尽,希望可以帮到读者们哦。

其他专栏:《MySQL》《C语言》《javaSE》《数据结构》等

内容分享:本期会对JavaEE中一个关于多线程的重要类Thread进行分享~

目录

什么是Thread

创建线程

继承Thread类

实现Runnable接口

匿名内部类创建Thread子类对象

匿名内部类创建Runnable子类对象

lambda表达式创建子类对象

Thread类的方法与常见属性

构造方法

常见属性

常用方法

启动线程 - start()

中断线程 

引入标记

interrupt()

异常的原因

等待程序 - join()

获取当前线程引用 - currentThread()


什么是Thread

Thread类是在Java标准库中的,它可以视为是对操作系统提供的API进一步的抽象与封装. 一个Thread实例对象我们可以认为是一个线程.

创建线程

继承Thread类

这里需要继Thread类来创建一个线程类. 因为这里要重写run方法.里面就是这个线程需要执行的逻辑.

然后还需要创建它的实例,这样一个线程才算创建出来了. 最后需要调用start方法启动线程.

class MyThread extends Thread {@Overridepublic void run() {System.out.println("hello Thread");}
}
public class ThreadDemo1 {public static void main(String[] args) {Thread t = new MyThread();t.start();System.out.println("hello main");}
}

实现Runnable接口

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

class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("线程运行代码");}
}
public class ThreadDemo2 {public static void main(String[] args) {Thread t = new Thread(new MyRunnable());t.start();System.out.println("hell main");}
}

匿名内部类创建Thread子类对象

这里后面是Thread类的匿名子类.

public class ThreadDemo3 {public static void main(String[] args) {Thread t = new Thread("这是我"){@Overridepublic void run() {System.out.println("这是匿名方法");while(true) {System.out.println("heeh");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}};t.start();}
}

匿名内部类创建Runnable子类对象

public class ThreadDemo4 {public static void main(String[] args) {Thread t = new Thread(new Runnable(){@Overridepublic void run() {System.out.println("匿名创建Runnable子类");}});t.start();}
}

lambda表达式创建子类对象

public class ThreadDemo5 {public static void main(String[] args) {Thread t = new Thread(() -> {System.out.println("lambda表达式创建子类对象");});t.start();}
}

Thread类的方法与常见属性

构造方法

这里第三个和第四个可以重命名,这样可以更好的进行调试.

常见属性

ID是这个线程的唯一表示,不同的线程ID是不会重复的

名称一般就是调试的时候可以用到

状态表示一个线程当前所处的情况.一般有就绪状态和堵塞状态

优先级表示线程是不是更容易被调度

前台线程的运行会阻止进程的结束,后台线程的运行不会阻止进程的结束.一个进程的结束需要等所有前台线程执行完才会结束,不然就算是main线程执行完了也不会结束.我们创建的线程默认都是前台线程.

是否存活简单来说就是run方法是否执行完了. Java中Thread实例虽然代表的是多线程,但是它的生命周期和内核中创建出来的线程PCB的生命周期不是一样的.

此时虽然Thread实例对象有了,但是内核中线程PCB还没创建,isAlive是false.只有在t.start()执行后内核中的PCB才会创建出来,这时isAlive为true

当run方法执行完后,内核中的PCB就销毁了,这时isAlive为false.

常用方法

启动线程 - start()

这里Thread实例对象虽然创建出来了,但是线程并未真正的启动.调用start()后才算真正的创建出了一个线程. 这里的start()才是在内核中创建出一个线程. 调用start()创建线程本质上就是调用系统的API来创建线程.

这里一个线程只能调用一次start(),再次使用start需要用另一个线程对象来调用.不然它会报出一个非法线程状态的异常.

public class ThreadDemo1 {public static void main(String[] args) {Thread thread = new Thread() {@Overridepublic void run() {System.out.println("线程");}};thread.start();thread.start();System.out.println("hell main");}
}

中断线程 

中断一个线程就是提前让这个线程的run方法结束.常用的方法有两种:

1. 通过引入一个标记

2. 通过interrupt()方法

引入标记

这里通过flag这个标记来让run方法提前结束.当main线程执行到flag = true时,另一线程就会提前结束.

public class ThreadDemo2 {public static  boolean flag = false;public static void main(String[] args) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {while(!flag) {System.out.println("hell Thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}});thread.start();try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("终止这个线程");flag = true;}
}

这里注意,flag标识是不能在main方法内中,不能作为局部变量.虽然lambda匿名内部类可以通过变量捕捉访问到外面的局部变量.但是这个局部变量必须是不可变的,是final修饰的,这就和我们需要通过改变标记来终止线程发生冲突了,这就不可行.

必须是final是因为main方法和Thread都有自己的函数栈帧.他们生命周期不同.flag是在属于main的栈帧中,一但main执行完了,它的栈帧就会销毁,Thread再想使用就用不到了. 这里变量捕捉就是为了这个而诞生.它就是传参,本质上就是在需要的线程上将flag拷贝一份.为了保证flag的一致性就让它不能改变.

interrupt()

Thread里面自带一个标志位.我们可以通过方法来获取和改变这个标志位.

这里可以用Thread.interrupted()或者Thread.currentThread().isInterrupted()来获取内置的标志位.

interrupt()方法可以改变标志位.

这里使用了Thread.currentThread().isterrupted:

public class ThreadDemo3 {public static void main(String[] args) {Thread thread = new Thread(() -> {while(!Thread.currentThread().isInterrupted()) {System.out.println("hell Thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});thread.start();try {Thread.sleep(4000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("中断它");thread.interrupt();}
}

但运行代码后我们会发现问题:

这里会发现它会抛出一个中断异常,然后继续运行,并没有停下来.

异常的原因

这时因为这里interrupt()方法提前唤醒了sleep,这个时候sleep就会做两件事:

1. 抛出InterruptedExecption这个异常

2. 将内置标志位还原.

所有这会导致线程继续运行.

需要线程停下来,处理方法我们在catch里面加上break就可以了.

这里在catch中我们有三种处理方法:

1. 让线程立刻停下来

2. 让线程先运行一些代码再停下来

3. 让线程不停下来继续运行

 我们处理异常也有几种常见的方法:

等待程序 - join()

join方法可以调整线程执行的先后顺序.虽然说线程的调度执行是随机调度的.但这里join可以将线程进行堵塞从而影响到了线程的执行先后顺序.join所在的线程会发生堵塞,在调用join方法线程运行完后,这个线程的堵塞状态才会解除.

注意: 这里是调用join()的线程被等待先执行,join()所在的线程等待,后执行.

join()方法有三种:

第一种: 死等,需要等等待的线程执行完才会解除堵塞状态

第二种: 有时间的等待, 在一定时间内进行堵塞.超出时间范围就会解除堵塞状态

第三种:精确到微秒的有时间等待.

一般情况下,我们最常用的就是第二种情况:

public class ThreadDemo4 {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(() -> {while(true) {System.out.println("hell main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread.start();thread.join();while(true) {System.out.println("hell ");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

这段代码,就是main线程进入堵塞状态,等待Thread线程结束. 虽然说Thread这个线程这执行中可以和其他多个线程共同进行调度执行,但由于main线程一直在等待,就算Thread线程在CPU上进行了多次切换也不影响这个线程先执行完.

这里注意: 我们的interrupt方法可以将join线程提前唤醒.

获取当前线程引用 - currentThread()

 在使用类继承Thread创建线程方法我们可以用this直接引用这个对象.但是当使用lambda/匿名内部类/Runnable时this就不再指向Thread对象了.这时我们获取Thread对象引用就需要使用currentThread()方法了. 

public class ThreadDemo7 {public static void main(String[] args) {Thread t = new Thread(() -> {System.out.println("hello Thread");System.out.println(Thread.currentThread().getName());});t.start();}
}

休眠当前线程 - sleep() 

sleep可以将当前线程休眠一定时间,这个时间可以自己设定.但使用它需要抛出异常或者try- catch.这里休眠的时间因为线程调度的不可控,一般都会大于等于设定的时间.

它也有两种方法:

一般都是使用第一种,第二种是精确到微秒.

public class ThreadDemo8 {public static void main(String[] args) {Thread t = new Thread(() -> {try {Thread.sleep(1111);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("休眠1.111秒");});t.start();}
}

相关文章:

【JavaEE】Java中的多线程 (Thread类)

作者主页:paper jie_博客 本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。 本文录入于《JavaEE》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力)打造&…...

python中具名元组的使用

collections.namedtuple是一个工厂函数,它可以用来构建一个带字段名的元组和一个有名字的类。 from collections import namedtuple City namedtuple(City2, name country population coordinates) tokyo City(Tokyo, JP, 36.933, (35.689722, 139.691667)) pr…...

【开题报告】基于SpringBoot的婚纱店试妆预约平台的设计与实现

1.选题背景 婚礼是人生中的重要时刻,而试妆是婚礼准备过程中不可或缺的一环。传统的婚纱店试妆预约方式通常需要亲自到店或通过电话预约,这样的方式可能存在一些问题。首先,用户需要花费时间和精力到店进行预约,对于忙碌的现代人…...

Qt 布局讲解及举例

Qt布局是一个用于管理窗口部件位置和大小的机制,它使得开发人员能够轻松地创建可伸缩、可调整大小的界面。在Qt中,布局管理器是一种用于自动调整窗口部件大小的机制,它可以根据窗口大小的变化自动调整部件的位置和大小。 Qt布局管理器通过使…...

【微服务】java 规则引擎使用详解

目录 一、什么是规则引擎 1.1 规则引擎概述 1.2 规则引擎执行过程 二、为什么要使用规则引擎 2.1 使用规则引擎的好处 2.1.1 易于维护和更新 2.1.2 增强应用程序的准确性和效率 2.1.3 加快应用程序的开发和部署 2.1.4 支持可视化和可管理性 2.2 规则引擎使用场景 三、…...

HCIA-Datacom跟官方路线学习

通过两次更换策略。最后找到最终的学习方案,华为ICT官网有对这个路线的学习,hcia基础有这个学习路线,hcip也有目录路线。所以,最后制定学习路线,是根据这个认证的路线进行学习了: 官网课程:课程…...

MySQL三大日志详细总结(redo log undo log binlog)

MySQL日志 包括事务日志(redolog undolog)慢查询日志,通用查询日志,二进制日志(binlog) 最为重要的就是binlog(归档日志)事务日志redolog(重做日志)undolog…...

XXL-Job详解(二):安装部署

目录 前言环境下载项目调度中心部署执行器部署 前言 看该文章之前,最好看一下之前的文章,比较方便我们理解 XXL-Job详解(一):组件架构 环境 Maven3 Jdk1.8 Mysql5.7 下载项目 源码仓库地址链接: https://github.…...

支持Arm CCA的TF-A威胁模型

目录 一、简介 二、评估目标 2.1 假定 2.2 数据流图 三、威胁分析 3.1 威胁评估 3.1.1 针对所有固件镜像的一般威胁 3.1.2 引导固件可以缓解的威胁...

【Web端CAD/CAE文字标注】webgl+canvas 2d实现文字标注功能

一、需求背景 在CAD/CAE领域经常会遇到显示节点编号这种需求,效果如下图: 本文介绍如何在WebGL中实现文字的显示,对于如何在OpenGL中实现请绕路。 二、实现原理 Canvas是HTML5提供的元素,用于在网页上绘制图形,其支…...

对话框、内部控件位置

一、了解下几个函数 1、movewindow 了解下:MoveWindow 自己塞进去的是屏幕坐标 CrvtFaultRodDlg* dlg new CrvtFaultRodDlg();if (dlg ! NULL){BOOL ret dlg->Create(IDD_DlgCrvtFaultRod, NULL);if (ret) //Create failed.{RECT rect;{RECT rect1;dlg->…...

【GraphQL 】将GraphQL API添加到Postgres数据库的六种简单方法,比较Hasura、Prisma和其他

PostgreSQL是世界上最流行的开源SQL数据库之一,GraphQL是一种日益流行的API规范。 将经过验证和众所周知的PostgreSQL与GraphQL带来的API创建新方式集成在一起不是很好吗? 在本文中,我们讨论了六个不同的项目,它们试图将SQL与Gr…...

每日一题(LeetCode)----哈希表--有效的字母异位词

每日一题(LeetCode)----哈希表–有效的字母异位词 1.题目(242. 有效的字母异位词) 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互…...

【设计模式】行为型模式-第 3 章第 6 讲【中介者模式】

目录 定义 场景描叙 目的 主要解决 实现 基本类图 案例代码...

Django 通过 Trunc(kind) 和 Extract(lookup_name) 参数进行潜在 SQL 注入 (CVE-2022-34265)

漏洞描述 Django 于 2022 年6月4 日发布了一个安全更新,修复了 Trunc() 和 Extract() 数据库函数中的 SQL 注入漏洞。 参考链接: Django security releases issued: 4.0.6 and 3.2.14 | Weblog | Djang…...

Vue3-toRef 和 toRefs 函数

Vue3-toRef 和 toRefs 函数 功能:可以简化语法调用。toRef 函数执行时会生成一个对象 ObjectRefImpl ,是一个引用对象,具有value属性(getter 和 setter 属性)语法格式:toRef(对象名, 对象中的属性名) toRe…...

STM32---时钟树

写在前面:一个 MCU 越复杂,时钟系统也会相应地变得复杂,如 STM32F1 的时钟系统比较复杂,不像简单的 51 单片机一个系统时钟就 可以解决一切。对于 STM32F1 系列的芯片,其有多个时钟源,构成了一个庞大的是时…...

【功能测试】软件系统测试报告

1.引言 1.1.目的 本测试报告为 xxx 系统测试报告,本报告目的在于总结测试阶段的测试及测试结果分析,描述系统是否达到需求的目的。 本报告预期参考人员包括测试人员、测试部门经理、开发人员、项目管理人员等。 1.2.参考文档 《xxxx系统需求规格说明…...

CentOS一键安装docker脚本

CentOS安装Docker一键脚本 在CentOS上安装Docker是许多项目中常见的任务之一。为了简化这个过程,你可以使用下面的一键脚本。 #!/bin/bash# 卸载旧版本(如果有) sudo yum remove -y docker \docker-client \docker-client-latest \docker-c…...

PostGIS学习教程八:空间关系

PostGIS学习教程八:空间关系 到目前为止,我们只使用了测量(ST_Area、ST_Length)、序列化(ST_GeomFromText)或者反序列化(ST_AsGML)几何图形(geometry)的空间…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

多模态大语言模型arxiv论文略读(108)

CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题&#xff1a;CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者&#xff1a;Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

Xela矩阵三轴触觉传感器的工作原理解析与应用场景

Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知&#xff0c;帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量&#xff0c;能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度&#xff0c;还为机器人、医疗设备和制造业的智…...

Ubuntu系统多网卡多相机IP设置方法

目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机&#xff0c;交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息&#xff0c;系统版本&#xff1a;Ubuntu22.04.5 LTS&#xff1b;内核版本…...

云安全与网络安全:核心区别与协同作用解析

在数字化转型的浪潮中&#xff0c;云安全与网络安全作为信息安全的两大支柱&#xff0c;常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异&#xff0c;并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全&#xff1a;聚焦于保…...

Spring AOP代理对象生成原理

代理对象生成的关键类是【AnnotationAwareAspectJAutoProxyCreator】&#xff0c;这个类继承了【BeanPostProcessor】是一个后置处理器 在bean对象生命周期中初始化时执行【org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization】方法时…...