【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();}
}

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

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

相关文章

【Docker】安装MySQL 通俗易懂 亲测没有任何问题

目录 1.拉取镜像 2.运行容器 3.创建mysql配置文件 4.测试 1.拉取镜像 dockerhub官网:Docker 如果需要其他版本mysql docker pull mysql:xxx(版本) docker pull mysql #默认拉取最新版本 latest 2.运行容器 docker run -d -p 3306:33…

论文公式工具

论文公式工具 https://www.latexlive.com/home## 论文图片识别公式网页工具,免费的方便但是有限制次数,一天只能用三次公式图片识别。 先注册登录 我们到论文中截取一张图片 在识别得到的一串码中,删掉前面没用的 输出为这个格式&#x…

【服务器能干什么】搭建一个短网址平台,可以查看数据详情!

昨天在 YouTube 上看到又一个搭建自己短网址的视频教程,用的是开源的 polr,但是按照步骤一步步搭建下来,最后一步都会出现 顺哥轻创 PLAINTEXT Whoops, looks like something went wrong百度、谷歌查了一圈也没找到有效的解决方法。&#x…

【好书推荐-第30期】开发者请注意!因果推断与机器学习,终于有人能讲明白啦!

本文目录 一、因果推断二、因果推断的前世今生三、总结四、赠书条件 今天给各位读者推荐一本好书:《机器学习高级实践:计算广告、供需预测、智能营销、动态定价》,好书链接。 2023年初是人工智能爆发的里程碑式的重要阶段,以Open…

【Web】攻防世界Web_php_wrong_nginx_config

这题考察了绕过登录、目录浏览、后门利用 进来先是一个登录框,随便怎么输前端都直接弹窗 禁用js后再输入后登录 查看源码,好家伙,不管输什么都进不去 直接扫目录 访问/robots.txt 访问/hint.php 访问/Hack.php 抓包看一下 cookie里isLogin0…

机器学习【04重要】pycharm中关闭jupyter服务器

直接关掉pycharm 不行 点红方块关闭 不行 我们曲线进行 我们的方法成功截图 实现全程不在服务器上操作 首先点击下图 点击退出,即可 查看端口

java Swing UI设置统一字体大小

编写一个遍历组件设置字体大小的方法 public static void setUIFont() {Font f new Font("宋体", Font.PLAIN, 18);String names[] {"Label", "CheckBox", "PopupMenu", "MenuItem", "CheckBoxMenuItem", &quo…

人力资源管理后台 === 上传+权限数据

目录 1.员工详情-封装员工头像组件 2.员工详情-上传图片-创建腾讯云存储桶 3.员工详情-使用cos-sdk完成上传 4. 权限管理-搭建权限页面 5.权限管理-获取数据转化树形 6.权限管理-作业 7.权限应用-权限概念 8.权限应用-员工分配角色-弹出层 9.权限应用-员工分配角色-回…

【EMFace】《EMface: Detecting Hard Faces by Exploring Receptive Field Pyramids》

arXiv-2021 文章目录 1 Background and Motivation2 Related Work3 Advantages / Contributions4 Method5 Experiments5.1 Datasets and Metrics5.2 Ablation Study5.3 Comparison with State-of-the-Arts 6 Conclusion(own) 1 Background and Motivatio…

Spring 中存储 Bean 的相关注解

Bean的存 IoC控制反转,就是将对象的控制权交给Spring的IOC容器,由IOC容器创建及管理对象。 也就是bean的存储 类注解:五大注解 Controller(控制器存储) Service(服务存储) Component(组件存储…

深思:C与C++相互调用问题

背景 上周,偶然看到同事愁眉苦脸的样子,便善意咨询了下发生了什么。简单沟通下,才知道他遇到了一个工程编译的问题,一直无法编译通过,困扰了他快一天时间。出于个人的求知欲和知识的渴望,我便主动与他一同分…

【数据结构】堆的实现

目录 1. 前言2. 堆的实现2.1 初始化2.2 插入2.2.1 分析2.2.1.1 情况一2.2.1.2 情况二2.2.1.3 情况三 2.2.2 插入代码实现2.2.2.1 向上调整代码 2.3 删除2.3.1 分析2.3.2 删除代码实现2.3.2.1 向下调整代码 2.4 找根节点数据2.5 元素个数2.6 判空2.7 销毁 3. 源代码3.1 Heap.h3.…

许战海战略文库|主品牌升级为产业技术品牌,引领企业全球化发展

在当今高速发展的全球经济中,企业品牌已经成为其核心资产之一。这不仅仅是因为品牌可以为消费者带来识别度,更重要的是,它们可以为企业带来深厚的竞争壁垒。但对于许多企业来说,特别是技术密集型企业,仅仅依靠主品牌的…

如何让消费者接受品牌,口碑营销怎么做?

当新品牌进入小红书时,如何进行口碑营销是一个重要的问题。很多新品牌在刚刚进入小红书时,对于一些敏感时机把握的不准其实本质上,就是不明白什么阶段该做什么事。今天分享的就是如何让消费者接受品牌,口碑营销怎么做?…

NX二次开发UF_CURVE_ask_spline_data 函数介绍

文章作者:里海 来源网站:https://blog.csdn.net/WangPaiFeiXingYuan UF_CURVE_ask_spline_data Defined in: uf_curve.h int UF_CURVE_ask_spline_data(tag_t spline_tag, UF_CURVE_spline_p_t spline_data ) overview 概述 Reads the spline data a…

Java核心知识点整理大全18-笔记

Java核心知识点整理大全-笔记_希斯奎的博客-CSDN博客 Java核心知识点整理大全2-笔记_希斯奎的博客-CSDN博客 Java核心知识点整理大全3-笔记_希斯奎的博客-CSDN博客 Java核心知识点整理大全4-笔记-CSDN博客 Java核心知识点整理大全5-笔记-CSDN博客 Java核心知识点整理大全6…

Linux驱动开发——网络设备驱动(理论篇)

目录 一、前言 二、网络层次结构 三、网络设备驱动核心数据结构和函数 一、前言 网络设备驱动是 Linux 的第三大类驱动,也是我们学习的最后一类 Linux 驱动。这里我们首先简单学习一下网络协议层次结构,然后简单讨论 Linux 内核中网络实现的层次结构。…

接口测试工具(Jmeter)必学技巧

安装 使用JMeter的前提需要安装JDK,需要JDK1.7以上版本目前在用的是JMeter5.2版本,大家可自行下载解压使用 运行 进入解压路径如E: \apache-jmeter-5.2\bin,双击jmeter.bat启动运行 启动后默认为英文版本,可通过Options – Cho…

【知网稳定检索】2024年应用经济学,管理科学与社会发展国际学术会议(AEMSS 2024)

2024年应用经济学,管理科学与社会发展国际学术会议(AEMSS 2024) 2024 International Conference on Applied Economics, Management Science and Social Development 2024年应用经济学,管理科学与社会发展国际学术会议&#xff…

uniapp在H5端实现PDF和视频的上传、预览、下载

上传 上传页面 <u-form-item :label"(form.ququ3 1 ? 参培 : form.ququ3 2 ? 授课 : ) 证明材料" prop"ququ6" required><u-button click"upload" slot"right" type"primary" icon"arrow-upward" t…