JavaEE之多线程编程:2.创建线程及Thread类常见方法(超全!!!)

一、创建线程

Java中创建线程的写法有很多种!!!这里介绍其中5种。

方法1:继承Thread类,重写run

创建一个类,让这个类继承自Thread父类,再重写我们的run方法就可以了。
使用Thread类,不需要import别的包,因为它是再Java.lang下面的。

//写一个类,继承自标准库的Thread
class MyThread extends Thread {@Overridepublic void run() {System.out.println("hello word!");}
}
public class ThreadDemo1 {public static void main(String[] args) {//创建线程,是希望线程成为一个独立的执行流(执行一段代码)//创建线程是相当于雇了个人帮我们干活Thread t = new MyThread(); //这里就不用new标准库的thread的了,而是刚才创建的子类t.start(); //线程中的特殊方法,启动一个线程}
}

注意:
start() 是创建了一个新的线程,由新的线程来执行t.run()方法。

这个新的线程就是调用操作系统的API
通过操作系统内核创建新线程的PCB,并且把要执行的指令交给这个PCB,当PCB被调度到了CPU上执行的时候,也就执行到了线程run方法的代码了。

如果只是在main方法中输出"hello world",你的Java进程主要就是有一个线程(调用main方法的线程),主线程通过t.start(),主线程调用stat(),创建出一个新的线程,新的线程调用t.run(),如果我们run()方法执行完毕,这个线程自然销毁了。

方法2:实现Runnable接口

//Runnable 作用是描述一个“要执行的任务”,run 方法就是任务的执行细节。
class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("hello thread");}
}
public class ThreadDemo2 {public static void main(String[] args) {//这只是描述了个任务Runnable runnable = new MyRunnable();//把任务交给线程来执行Thread t = new Thread(runnable);t.start();}
}

解耦合。目的就是为了让 线程和任务(线程要干的活)之间分离开。
未来如果要改代码不用多线程,使用多进程、线程池或协程……此时代码改动比较小

方法3:使用匿名内部类,继承Thread

//使用匿名内部类来创建线程
public class ThreadDemo3 {public static void main(String[] args) {Thread t = new Thread() {@Overridepublic void run() {super.run();System.out.println("hello");}};t.start();}
}

其中new Thread()
①创建了一个Thread子类,(子类没有名字)所以才叫做“匿名”;
②创建了子类的实例,并且让 t 引用指向该实例。

方法4:使用匿名内部类,实现Runable

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

这个写法和方法2本质相同,只不过把实现Runable任务交给匿名内部类的语法。
此处是创建了一个类,实现Runable,同时创建了类的实例,并且传给Thread的构造方法。

方法5:使用Lambda表达式

此方法是最简单,最推荐的方法。

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

把任务用lambda表达式来描述,直接把lambda传给Thread构造方法。
lambda就是个匿名函数(没有名字的函数),用一次就没有了。
() - > {}

二、Thread类及常见方法

Thread类是JVM用来管理线程的一个类,换句话说,每个线程都有一个Thread对象与之相关联
可以说,每个执行流,都需要有一个对象来描述,类似下图,而Thread类的对象,就是用来描述一个线程执行流的,JVM会将这些Thread对象组织起来,用于线程调度,线程管理。
在这里插入图片描述

1. Thread的常见构造方法

在这里插入图片描述
其中:
最后两个Thread(String name)Thread(Runnable target, String name) 多了个name参数,这个参数是为了方便调试而给线程起了个名字。
线程的默认的名字为 thread-0,1,2之类的。

构造方法的格式如下:

Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread(“这是我的名字”);
Thread t4 = new Thread(new MyRunnable(), “这是我的名字”);

例如:

public class ThreadDemo6 {public static void main(String[] args) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {while (true) {System.out.println("hello");}}},"mythread");t.start();}
}

2. Thread的几个常见属性

在这里插入图片描述

  • ID是线程的唯一标识,不同线程不会重复;

  • 名称是各调试工具用到的;

  • getStat:构造方法里起的名字;

  • getStat:线程状态,Java中线程的状态要比操作系统原生的状态更丰富一些;

  • getPrior:线程的优先级可以获取,也可以设置,但是设置了不管用,因为优先级是很多要素影响得到的;

  • isDaem:是否守护线程,是否“后台线程”
    前台线程会阻止进程结束,前台线程的工作没做完,进程是完不了的;
    后台线程,不会阻止线程结束,后台线程没做完,进程是可以结束的。
    代码里手动创建的线程,默认都是前台线程,包括main,默认也是前台;
    其他JVM自带的线程都是后台线程。
    也可以手动使用setDaemon设置成后台线程,是后台线程,就是守护线程。
    在这里插入图片描述
    把t设置成 守护/后台线程,此时进程的结束与否就和t无关了。

  • isAlive()是否存活:判断当前系统中的这个线程是不是有了。
    在用户态(应用程序)中创建了一个线程Thread t = new Thread();
    程序中通过t.start();来调用,
    在真正调用start之前,调用t.isAlive(),此时是false,此时内核态(操作系统内核)里没有这个线程;
    而调用start后,就会让内核创建一个PCB,此时这个PCB才表示一个真正的线程,此时isAlive是 true。
    也可以简单的理解为start/run方法是否允许结束了。

    另外,如果内核里线程把run执行完了,此时线程销毁,PCB随之释放。但是Thread t 这个对象还不一定被释放,此时isAlive也是false。
    【总结】
    如果 t.run还没执行,isAlive 为 false
    如果 t,run 正在执行,isAlive 为 true
    如果 t.run执行结束,isAlive 为 false

    通过上述我们可以了解,Thread t这个对象比内核里的PCB存在的周期要久。

3. 启动一个线程——start()

之前我们已经看到了如何通过复写 run 方法创建一个线程对象,但线程对象被创建出来并不意味着线程就开始运行了。

  1. 复写run方法是提供给线程要做的事情的指令清单;
  2. 线程对象可以认为是把李四、王五等新线程叫过来了;
  3. 而调用start()方法,就是相当于喊一声“行动起来!”,线程才真正独立去执行了。

总之,run方法是描述了我们要做啥任务,而stat才是真正开始任务。
调用start方法,才真正在操作系统的底层创建出一个线程!

4. 终止一个线程

终止线程的意思是,不是让线程立即就停止,而是通知线程,你应该要停止了,但是是否真的停止,取决于线程这里的具体写法。终止线程有以下两种方式:

  1. 使用标志位来控制线程是否要停止;
public class ThreadDemo8 {private static boolean flag = true;public static void main(String[] args) throws InterruptedException{Thread t = new Thread(() -> {while (flag) { //当flag为true的时候System.out.println("hello!!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t.start();Thread.sleep(3000); //当执行到第三秒的时候,把flag变为false,结束循环flag = false;}
}

在这里插入图片描述
这个代码之所以能够起到作用,修改flag,t线程就结束,完全取决于t线程内部的代码,代码里通过flag控制循环。
因此,这里只是告诉让这个线程结束,这个线程是否要结束,什么时候结束,都是线程内部自己代码来决定的。因为是while(flag)所以3秒钟后结束了,要是while(true),怎么改都不会结束循环。

此方法的缺点:自定义变量这种方式,不能及时相应,尤其在sleep休眠的时间比较久的时候。

  1. 使用Thread自带的标志位( interrupt() 方法),来进行判定是否要停止。

这里调用interrupt,只是通知终止,不是线程一定要乖乖终止!!
【格式】

while (!Thread.currentThread().isInterrupted()) {}

其中:
① while判定,判断条件是true,则执行方法体内的循环,为false,则循环结束;
Thread.currentThread() :这是Thread类的静态方法,通过这个方法可以获取到当前线程。哪个线程调用这个方法,就是得到哪个线程的对象引用(类似于this);
isInterrupted() 为true表示被终止,为false表示未被终止,继续执行;
④ 前面有个 ! 逻辑取反符,所以当isInterrupted() 为true时,!isInterrupted() 就为false,反之,则为true。

interrupt 会做两件事:
① 把线程内部的标志位(boolean)给设置成 true;
② 如果线程在进行sleep,就会触发异常,把sleep给唤醒。

但是sleep在唤醒的时候,还会做一件事,就是把刚在设置的这个标志位,再设置回false。(清空了标志位),这就导致,当sleep的异常被catch完了之后,循环继续执行!
如下述例子:

【例1】:线程t忽视了终止请求

public class ThreadDemo9 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {System.out.println("hello thread!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t.start();Thread.sleep(3000);t.interrupt(); //3秒后,把线程内部的标志位(boolean)给设置成 true}
}

在这里插入图片描述
此时循环会一直执行下去!这里就是sleep清空的例子。

【例2】:线程t立即响应了终止请求(加了break)

public class ThreadDemo9 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {System.out.println("hello thread!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();break;}}});t.start();Thread.sleep(3000);t.interrupt(); //3秒后,把线程内部的标志位(boolean)给设置成 true}
}

在这里插入图片描述

【例3】:稍后进行终止
执行完等了3秒钟,代码执行完毕。

public class ThreadDemo9 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {System.out.println("hello thread!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();//稍后再终止!try {Thread.sleep(3000);} catch (InterruptedException ex) {ex.printStackTrace();}break;}}});t.start();Thread.sleep(3000);t.interrupt();}
}

【总结】
大前提:调用interrupt,只是告诉线程你该终止了,但是它是不是真的终止,这是它自己的事情。
注意其中sleep有个清楚标志位的情况,唤醒之后,线程终不终止,立即还是稍后终止,就把选择全交给程序员自己了。

5. 等待一个线程

有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作,这是我们需要一个方法明确等待线程的结束
可理解为等待一个线程结束!
线程是一个随机调度的过程,等待线程做的事,就是再控制两个线程结束的顺序。

【例1】

public class ThreadDemo10 {public static void main(String[] args) {Thread t = new Thread(() -> {for (int i = 0; i < 3; i++) {System.out.println("hello!!!");}try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});t.start();System.out.println("join 之前");//此处的join就是让当前的main线程来等待t线程执行结束(等待t的run执行完)try {t.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("join之后");}
}

在这里插入图片描述
执行过程:
t 调用了start()后,t 线程和 main主线程就并发执行,分头行动;
主线程这里打印了“join之前”,同时t线程打印了hello thread;
我们的t线程在执行过程中,主线程并没有打印join之后,而是在join这里等待了一会(发生阻塞block),等待3s之后,t线程执行完了,我们的主线程才会执行后面的 “join之后”;
主线程,等待t线程彻底执行完毕之后,才继续往下执行了。
通过输出,我们也能看出 t 线程肯定比 main 线程先结束。

【例2】

public class ThreadDemo10 {public static void main(String[] args) {Thread t = new Thread(() -> {for (int i = 0; i < 3; i++) {System.out.println("hello!!!");}try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});t.start();try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("join 之前");//此处的join就是让当前的main线程来等待t线程执行结束(等待t的run执行完)try {t.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("join之后");}
}

在这里插入图片描述
在join前加了一个等待5s钟,此时在执行join的时候,t 已经结束了,所以join不会堵塞,就会立即返回。
在这里插入图片描述

  • public void join() :无参版本,一直等;
  • public void join(long millis) :指定一个超时时间(最大等待时间),这种是最常见的,一直等很容易又问题。

【总结】
线程没结束,就等待,线程结束了,就立即返回;
总之可以保证这两个线程的返回顺序。

6. 获取当前线程引用

public static Thread currentThread();
返回当前线程对象的引用。

调用这个方法,不需要实例,直接通过类名来调用

	Thread t = new Thread();Thread.currentThread();

在哪个线程中调用,就能获取到哪个线程的实例

7. 休眠当前线程

是我们比较熟悉的一组方法,有一点要记得,因为线程的调度是不可控的,所以,这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间的。

public static void sleep(long millis) throws InterruptedException 休眠当前线程 millis
毫秒
public static void sleep(long millis, int nanos) throws InterruptedException 可以更高精度的休眠

public class ThreadDemo {public static void main(String[] args) throws InterruptedException {System.out.println(System.currentTimeMillis());Thread.sleep(3 * 1000);System.out.println(System.currentTimeMillis());}
}

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

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

相关文章

MYsql第三次作业

目录 问题&#xff1a; 解答 1.查询student表的所有记录 2.查询student表的第2条到4条记录 3.从student表查询所有学生的学号&#xff08;id&#xff09;、姓名&#xff08;name&#xff09;和院系&#xff08;department&#xff09;的信息 4.从student表中查询计算机系和…

智能优化算法应用:基于鸽群算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于鸽群算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于鸽群算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.鸽群算法4.实验参数设定5.算法结果6.参考文献7.MA…

EdgeYOLO: anchor-free,边缘部署友好

简体中文 1 Intro 2 Updates 3 Coming Soon 4 Models 5 Quick Start \quad 5.1 setup

物奇平台MIC配置与音频通路关系

物奇平台MIC配置与音频通路关系 是否需要申请加入数字音频系统研究开发交流答疑群(课题组)&#xff1f;可加我微信hezkz17, 本群提供音频技术答疑服务&#xff0c;群赠送语音信号处理降噪算法&#xff0c;蓝牙耳机音频&#xff0c;DSP音频项目核心开发资料, 1 255代表无效&am…

uni-app 一些实用的页面模板

时间倒计时 <!-- 时间倒计时 --> <template><view class"container"><view class"flex-row time-box"><view class"time-item">{{ laveTimeList[0] }}</view><text>天</text><view class&qu…

Java笔记草稿——已完成

导航&#xff1a; 【Java笔记踩坑汇总】Java基础JavaWebSSMSpringBootSpringCloud瑞吉外卖/黑马旅游/谷粒商城/学成在线设计模式面试题汇总性能调优/架构设计源码-CSDN博客 推荐学习视频&#xff1a; 黑马程序员全套Java教程_哔哩哔哩 尚硅谷Java入门视频教程_哔哩哔哩 目录 零…

[BUUCTF 2018]Online Tool1

提示 利用nmap上传文件 首先进行代码分析&#xff1a; 首先是进行判断http信息头里是否在HTTP_X_FORWARDED_FOR并且是否有参数 $_SERVER[“HTTP_X_FORWARDED_FOR”] 的值才是客户端真正的IP&#xff08;如果是多层代理&#xff0c;该值可能是由客户端真正IP和多个代理服务…

二十五、图形视图框架

二十五、图形视图框架 我们将要用到三个类&#xff0c;QGraphicsView&#xff08;视图类&#xff09;、QGraphicsScene&#xff08;场景类&#xff09;、QGraphicsItem&#xff08;图元类&#xff09;。 QGraphicsView&#xff08;视图类&#xff09; 继承QWidget类&#xf…

玩转Docker(一):容器生态系统

文章目录 一、核心技术二、平台技术三、支持技术 本文结构如下&#xff1a; 一、核心技术 容器核心技术是指能够让Container在host上运行起来的那些技术。 &#xff08;1&#xff09;容器规范 容器不光是Docker&#xff0c;还有其他容器&#xff0c;比如CoreOS的rkt。为了保证…

网络推理之深度学习推理框架

如何选择深度学习推理框架&#xff1f; PyTorch vs LibTorch&#xff1a;网络推理速度谁更快&#xff1f; 高质量C进阶[2]&#xff1a;如何让线性代数加速1000倍&#xff1f; TensorRT: ONNX:

微服务--07--Sentienl中使用的限流算法

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 Sentienl中使用的限流算法1、计数器固定窗口算法2、计数器滑动窗口算法----&#xff08;默认&#xff09;3、漏桶算法----&#xff08;排队等待&#xff09;4、令牌…

node.js 启一个前端代理服务,代码直接改一改拿来用

文章目录 前言一、分析技术二、操作步骤2.1、下载依赖2.2、创建一个 serve.js 文件2.3、js 文件中写入以下代码 三、运行&#xff1a; node serve四、结果展示五、总结六、感谢 前言 有时候我们需要做一些基础的页面时&#xff0c;在研发过程中需要代理调用接口避免浏览器跨域…

AI全栈大模型工程师(二十六)如何选择 GPU 和云服务厂商

&#x1f4a1; 这节课会带给你 如何选择 GPU 和云服务厂商&#xff0c;追求最高性价比 如何部署自己 fine-tune 的模型&#xff0c;向业务提供高可用推理服务 如何控制内容安全&#xff0c;做好算法备案&#xff0c;确保合规 开始上课&#xff01; 硬件选型 当我们为模型训练及…

电子学会C/C++编程等级考试2022年12月(五级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:漫漫回国路 2020年5月,国际航班机票难求。一位在美国华盛顿的中国留学生,因为一些原因必须在本周内回到北京。现在已知各个机场之间的航班情况,求问他回不回得来(不考虑转机次数和机票价格)。 时间限制:1000 内存限制:655…

Ajax原理以及优缺点

Ajax原理 1.Ajax的原理简单来说是在用户和服务器之间加了—个中间层(AJAX引擎)&#xff0c;通过XmlHttpRequest对象来向服务器发异步请求&#xff0c; 2.从服务器获得数据&#xff0c;然后用javascript来操作DOM而更新页面。使用户操作与服务器响应异步化。 3.这其中最关键的一…

Linux系统使用ESP8266开发板(CP2102)

连接ESP8266开发板到电脑 虚拟机选择开发板硬件连接 查看USB连接情况: lsusb 授权USB接口访问 成功连接 编译项目 上传到开发板 成功提供WIFI热点服务

三、Shell 环境

一、Linux 系统分类 在 Linux 中&#xff0c;常见的 Shell 有以下几种&#xff1a; Bourne Shell&#xff08;sh&#xff09;&#xff1a;最早的 Shell&#xff0c;由 Stephen Bourne 开发。它是大多数其他 Shell 的基础。Bourne Again Shell&#xff08;bash&#xff09;&am…

thinkphp6入门(13)-- 一对多关联模型

定义一对一关联&#xff0c;例如&#xff0c;一个用户都有多个工作经历。 一、两表 1.用户表:user 2.工作经验表&#xff1a;work_experience user表的id关联work_experience表的user_id。 注意看&#xff0c;user_id1的有2条工作经验 二、数据模型 主表模型&#xff1a;…

如何在 1 天内将网站打造为手机app

为您的网站提供移动应用程序的重要性怎么强调都不为过。随着用户越来越依赖智能手机和平板电脑进行在线活动&#xff0c;将您的网站转变为移动手机app可以显着增强用户体验、提高参与度并扩大您的在线影响力。在这篇博客中&#xff0c;我们将探讨如何快速有效地将网站制作成移动…

【Let‘s Encrypt SSL】使用 acme.sh 给 Nginx 安装 Let’s Encrypt 提供的免费 SSL 证书

安装acme.sh 安装 acme.sh 并设置邮箱用来接受重要通知&#xff0c;如证书快过期未更新通知 curl https://get.acme.sh | sh -s emailmyexample.com执行命令后几秒就安装好了&#xff0c;如果半天没有反应请 CtrlC 后重新执行命令。acme.sh 安装在 ~/.acme.sh 目录下&#xf…