Java-宋红康-(P133-P134)-多线程创建方式(Thread and Runnable)

b站视频

133-多线程-线程创建方式1:继承Thread类_哔哩哔哩_bilibili

目录

3.1 继承Thread

3.1.1 继承Thread类方式

3.1.2 线程的执行流程

3.1.3 线程内存图

3.1.4 run()方法和start()方法

3.1.5 线程名字的设置和获取

3.1.6 获取运行main方法线程的名字

3.1.7 练习题

3.2 实现 Runable

3.2.1 实现Runnable接口方式

3.2.2 Thread和Runnable的对比

3.2.3 匿名内部类方式创建线程


3.1 继承Thread

3.1.1 继承Thread类方式

Java使用java.lang.Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码。Java使用线程执行体来代表这段程序流。Java中通过继承Thread类来创建启动多线程的步骤如下:

  1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。

  2. 创建Thread子类的实例,即创建了线程对象

  3. 调用线程对象的start()方法来启动该线程

自定义线程类:

class MyThread extends Thread{@Overridepublic void run() {for (int i = 0; i < 20; i++) {System.out.println("自定义线程正在执行!"+i);}}
}

测试类:

public class Main {public static void main(String[] args)  throws Exception{MyThread mt=new MyThread();mt.start();for (int i = 0; i < 20; i++) {System.out.println("main线程"+i);}}
}

结果:

3.1.2 线程的执行流程

程序启动运行main时候,java虚拟机启动一个进程,主线程main在main()调用时候被创建。随着调用mt的对象的start方法,另外一个新的线程也启动了,这样,整个应用就在多线程下运行。

通过这张图我们可以很清晰的看到多线程的执行流程,那么为什么可以完成并发执行呢?我们再来讲一讲原理。

多线程执行时,到底在内存中是如何运行的呢?以上个程序为例,进行图解说明:

多线程执行时,在栈内存中,其实每一个执行线程都有一片自己所属的栈内存空间。进行方法的压栈和弹栈。

3.1.3 线程内存图

当执行线程的任务结束了,线程自动在栈内存中释放了。但是当所有的执行线程都结束了,那么进程就结束了。

3.1.4 run()方法和start()方法

  • run()方法,是线程执行的任务方法,每个线程都会调用run()方法执行,我们将线程要执行的任务代码都写在run()方法中就可以被线程调用执行。

  • start()方法,开启线程,线程调用run()方法。start()方法源代码中会调用本地方法start0()来启动线程:private native void start0(),本地方法都是和操作系统交互的,因此可以看出每次开启一个线程的线程都会和操作系统进行交互。

注意:一个线程只能被启动一次!!

实例:

package test;public class TestDemo03 {public static void main(String[] args) {//创建当前Thread的子类的对象PrintNumber t1=new PrintNumber();//通过对象调用start()t1.start();for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+i);}}
}class PrintNumber extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+i);}}
}

结果:

问题1:能否用t1.run()方法替换t1.start()的调用,实现分线程的创建和调用。

package test;public class TestDemo03 {public static void main(String[] args) {//创建当前Thread的子类的对象PrintNumber t1=new PrintNumber();//通过对象调用start()// t1.start();t1.run();for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+i);}}
}class PrintNumber extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+i);}}
}

结果:(输出的全是main,通过调用run方法后变成了单线程)

3.1.5 线程名字的设置和获取

  • Thread类的方法String getName()可以获取到线程的名字。

  • Thread类的方法setName(String name)设置线程的名字。

  • 通过Thread类的构造方法Thread(String name)也可以设置线程的名字。

public class MyThread  extends Thread{public void run(){System.out.println("线程名字:"+super.getName());}
}

测试类:

public class Demo {public static void main(String[] args) {//创建自定义线程对象MyThread mt = new MyThread();//设置线程名字mt.setName("旺财");//开启新线程mt.start();}
}

注意:线程是有默认名字的,如果我们不设置线程的名字,JVM会赋予线程默认名字Thread-0,Thread-1。

3.1.6 获取运行main方法线程的名字

  • Demo类不是Thread的子类,因此不能使用getName()方法获取。

  • Thread类定义了静态方法static Thread currentThread()获取到当前正在执行的线程对象。

  • main方法也是被线程调用了,也是具有线程名字的。

public static void main(String[] args){Thread t = Thread.currentThread();System.out.println(t.getName());
} 

结果:

3.1.7 练习题

创建两个分线程,其中一个线程遍历100以内的偶数,另一个线程遍历100以内的奇数。

package test;public class TestDemo03 {public static void main(String[] args) {//创建当前Thread的子类的对象PrintNumber t1=new PrintNumber();//创建当前Thread的子类的对象PrintOddNumber t2=new PrintOddNumber();//通过对象调用start()t1.start();//通过对象调用start()t2.start();}
}//遍历100以内的偶数
class PrintNumber extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {if(i%2==0) {System.out.println(Thread.currentThread().getName() +"--" + i);}}}
}//遍历100以内的奇数
class PrintOddNumber extends Thread{@Overridepublic void run() {for (int i = 0; i < 100; i++) {if(i%2==1) {System.out.println(Thread.currentThread().getName() +"--"+i);}}}
}

结果:

3.2 实现 Runable

3.2.1 实现Runnable接口方式

采用java.lang.Runnable也是非常常见的一种,我们只需要重写run方法即可。

步骤如下:

  1. 创建一个实现Runnable接口的类

  2. 实现接口中的run()方法 --> 将此线程要执行的操作,声明在此方法体中

  3. 创建Runnable实现类的实例

  4. 将此对象实例作为参数传递到Thread类的构造器中,创建Thread类的实例

  5. Thread类的实例调用start()方法:①启动线程 ②调用当前线程的run()。

package test;public class TestDemo04 {public static void main(String[] args) {//③ 创建当前实现类的对象EvenNumberPrint evenNumberPrint=new EvenNumberPrint();//④ 将此对象作为参数传递到Thread类的构造器中,创建Thread类的实例Thread t1=new Thread(evenNumberPrint);//⑤ Thread类的实例调用start():1.启动线程 2. 调用当前线程的run()方法t1.start();for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+"---"+i);}}
}//① 创建一个实现Runnable接口的类
class EvenNumberPrint implements Runnable{//② 实现接口中的run() -->将此线程要执行的操作,声明在此方法中。@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+"---"+i);}}
}

结果:

通过实现Runnable接口,使得该类有了多线程类的特征。run()方法是多线程程序的一个执行目标。所有的多线程代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。

在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread对象的start()方法来运行多线程代码。

实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是继承Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的基础。

3.2.2 Thread和Runnable的对比

共同点:

  • 启动线程,使用的都是Thread类中定义的start()方法

  • 创建的线程对象,都是Thread类或其子类的实例。

不同点:

一个是类的继承,一个是接口的实现。

建议:建议使用实现Runnable接口的方式

Runnable方式的好处:

①实现的方式,可以避免类的单继承的局限性。

②更适合多个相同的程序代码的线程去共享同一个资源。

③增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。

联系: public class Thread implements Runnable(代理模式)

3.2.3 匿名内部类方式创建线程

使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。

使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法:

public class NoNameInnerClassThread {public static void main(String[] args) {                   
//                new Runnable(){
//                        public void run(){
//                                for (int i = 0; i < 20; i++) {
//                                        System.out.println("张宇:"+i);
//                                }
//                        }  
//                   }; //---这个整体  相当于new MyRunnable()Runnable r = new Runnable(){public void run(){for (int i = 0; i < 20; i++) {System.out.println("张宇:"+i);}}  };new Thread(r).start();for (int i = 0; i < 20; i++) {System.out.println("费玉清:"+i);}}
}

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

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

相关文章

Linux进程间通信之共享内存

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;Linux &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 本博客主要内容讲解共享内存原理和相关接口的介绍&#xff0c;以及一个…

更换cmd下默认选择Python解释器

问题 我的电脑里有多个Python解释器&#xff0c;一个是自己下载的python37&#xff0c;版本是3.7.0&#xff0c;一个是anaconda的base环境&#xff0c;版本是3.7.4&#xff0c;还有虚拟环境里的python解释器。 最近发现&#xff0c;在cmd下输入python&#xff0c;使用的是anac…

肺是人体的第一道防线,流感频发季节,最有效的养肺方法你得知道!

肺脏是人体的第一道防线&#xff0c;牵动着整个呼吸道的健康&#xff0c;一旦肺脏受损&#xff0c;易引发咳嗽、气喘甚至肺炎。在流感、呼吸道疾病高发的冬季&#xff0c;如何呵护肺脏&#xff0c;保持身体健康&#xff1f; 全民养肺&#xff0c;刻不容缓 养肺不仅仅是中老年朋…

深入浅出之中央空调体系架构及楼宇自控系统

一、关于建筑节能 1、建筑能耗 在中国&#xff0c;建筑能耗占社会总能耗45.5%。来源&#xff1a;《中国建筑能耗研究报告&#xff08;2022&#xff09;》 2、空调、采暖、照明占比最高 建筑节能是指在保证、提高建筑舒适性和生活质量的条件下&#xff0c;在建筑物使用的全过…

12.5 作业

1&#xff0c; 以下是一个简单的比喻&#xff0c;将多态概念与生活中的实际情况相联系&#xff1a; 比喻&#xff1a;动物园的讲解员和动物表演 想象一下你去了一家动物园&#xff0c;看到了许多不同种类的动物&#xff0c;如狮子、大象、猴子等。现在&#xff0c;动物园里有…

【Qt开发流程】之对象模型2:属性系统

描述 Qt提供了一个复杂的属性系统&#xff0c;类似于一些编译器供应商提供的属性系统。然而&#xff0c;作为一个独立于编译器和平台的库&#xff0c;Qt不依赖于非标准的编译器特性&#xff0c;如__property或[property]。 Qt解决方案适用于Qt支持的所有平台上的任何标准c编译…

力扣第374场周赛题解

这一场周赛的题目是比较难的一次&#xff0c;写了1个多小时就写了两个题目。 首先第一题&#xff1a; 纯水题&#xff0c;遍历然后进行一下判断就可以解决了。这边就不放代码了。 第二题&#xff1a; 这个题目&#xff0c;我觉得难度非常大&#xff0c;其实代码量也不大都是很…

IOday3作业

1> 使用文件IO完成对图像的读写操作 #include<myhead.h>int main(int argc, const char *argv[]) {//只读打开图片int fd-1;if((fd open("./R-C.bmp",O_RDWR))-1){perror("open");return -1;}//向后便宜两个字节找到大小的起始地址lseek(fd,2,S…

【数据结构】二叉树的实现

目录 1. 前言2. 二叉树的实现2.1 创建一棵树2.2 前序遍历2.2.1 分析2.2.2 代码实现2.2.3 递归展开图 2.3 中序遍历2.3.1 分析2.3.2 代码实现2.3.3 递归展开图 2.4 后序遍历2.4.1 分析2.4.2 代码实现2.4.3 递归展开图 2.5 求节点个数2.5.1 分析2.5.2 代码实现 2.6 求叶子节点个数…

Linux 调试器 --- g d b 使用

目录 一&#xff1a;gdb简介 二&#xff1a;示例代码 三&#xff1a;使用 1.启动gdb 2.各种指令 <1>: 查看源代码 <2>:设置断点 <3>:查看断点信息 <4>:删除断点 <5>: run <6>:逐过程调试 <7>:逐语句调试 <8>:查…

TrustZone​之在安全状态之间切换

如果处理器处于NS.EL1,而软件想要转移到S.EL1,应该如何实现呢? 要改变安全状态,无论是向上还是向下,执行都必须经过EL3,如下图所示: 前面的图表显示了在不同安全状态之间移动涉及的步骤的示例序列。逐步进行解释: 进入较高的异常级别需要一个异常。通常,此异常…

相关基础知识

本文引注&#xff1a; https://zhuanlan.zhihu.com/p/447221519 1.方差 2.自协方差矩阵 3.自相关矩阵 4.互协方差矩阵 5.互相关矩阵 6.相关系数 7.自相关函数、自协方差函数与功率谱密度 8.互相关函数、互协方差函数与互功率谱密度

时间选择器

<el-form-item label"时间范围"><!-- <el-date-picker size"small"v-model"createTime"type"daterange"range-separator"至"start-placeholder"请输入起始创建时间"end-placeholder"请输入终止创…

无线网优AP、SW发现控制器

目录 无线网优解决的问题 1、信号覆盖不足的原因 2、信道繁忙 3、非802.11干扰 4、协商速率低 5、漫游效果差 6、有线带宽阻塞 无线网优方法 交换机发现与激活 一&#xff0c;交换机发现控制器方式 1、二层广播 2、DHCP option43方式 3、DNS域名解析方式 4、trou…

基于springboot + vue大学生竞赛管理系统

qq&#xff08;2829419543&#xff09;获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;springboot 前端&#xff1a;采用vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xf…

【AIGC】AI作图最全提示词prompt集合(收藏级)

目录 一、正向和负向提示词 二、作图参数 你好&#xff0c;我是giszz. AI做图真是太爽了&#xff0c;解放生产力&#xff0c;发展生产力。 但是&#xff0c;你是不是也总疑惑&#xff0c;为什么别人的图&#xff0c;表现力那么丰富呢&#xff0c;而且指哪打哪&#xff0c;要…

DFT(离散傅里叶变换)的通俗理解

本文包含了博主对离散傅里叶变换&#xff0c;负频率&#xff0c;实信号与复信号频谱的理解&#xff0c;如有不妥&#xff0c;欢迎各位批评指正与讨论。 文章目录 DFT的理解信号的频谱实信号的频谱复信号的频谱 DFT的理解 傅里叶变换是一种将信号从时域转换到频域的数学工具。…

通信标准化协会,信通院及量子信息网络产业联盟调研玻色量子,共绘实用化量子未来!

8月14日&#xff0c;中国通信标准化协会&#xff0c;信通院标准所及量子信息网络产业联盟等单位领导走访调研北京玻色量子科技有限公司&#xff08;以下简称“玻色量子”&#xff09;&#xff0c;参观了玻色量子公司及自建的十万颗粒洁净度的光量子信息技术实验室&#x1f517;…

STM32下载程序的五种方法

刚开始学习 STM32 的时候&#xff0c;很多小伙伴满怀热情买好了各种设备&#xff0c;但很快就遇到了第一个拦路虎——如何将写好的代码烧进去这个黑乎乎的芯片&#xff5e; STM32 的烧录方式多样且灵活&#xff0c;可以根据实际需求选择适合的方式来将程序烧录到芯片中。本文将…

10年前,我就用 SQL注入方式发现了学校网站的漏洞

大家好&#xff0c;我是风筝。 事情是这样子的&#xff0c;在10年以前&#xff0c;某个月黑风高夜的夜里&#xff0c;虽然这么说有点暴露年龄了&#xff0c;但无所谓&#xff0c;毕竟我也才18而已。我打开电脑&#xff0c;在浏览器中输入我们高中学校的网址&#xff0c;页面很…