JavaEE 第1节 认识多线程

devtools/2024/9/23 8:53:57/

本节目标(全是重点,都必须掌握)

1、了解什么是线程、多线程、进程以及他们之间的关系

2、了解多线程的优势以及各种特性

3、用Java掌握多种创建线程的方法

一、线程、多线程、进程

1、概念

1.基本概念

这三个名词的概念可以用一个餐馆的厨房和厨师来进行超级形象的比喻

想象一下一个餐馆的厨房

整个厨房就是一个进程(Process)。这个厨房就是负责给前台客户提供各种菜品的进程。

厨房里得要有厨师吧,“厨房”里的“厨师”就叫做线程(Thread)

如果是一个大型酒店,厨房里的厨师肯定不止一位对吧,那么如果一个厨房里的厨师有好几位,既一个进程里又好几个线程,那么这就叫做多线程。

每一个厨师在厨房可以独自完成自己的任务,这就叫做线程的独立性

每一个厨师在厨房可以同时工作(做自己的菜),这就叫做线程的并行性线程之间是并发执行的

但是每一个厨师都在同一个厨房进行工作(同一个进程),公用一套厨具,一个厨师(线程)如果在用面包机,另一个厨师(线程)可能就不能用了(主要取决于“厨房”获得的资源大小),这就叫做同一进程中的所有线程,资源是共享的。

2.线程、多线程、进程之间的区别

我们在梳理一下刚才所讲的比喻:

厨房——>进程

厨师——>线程

一个厨房多个厨师——>多线程

  • 所以一个进程至少包含一个线程,既主线程。
     
  • 不同的进程之间是不共享资源的(比如硬盘,网络资源等),但是一个进程中的每一个线程共享同一份资源。
     
  • 线程是系统进行任务调度的最小单位。
     
  • 进程是系统进行资源分配的最小单位。
     
  • 一个进程挂了一般不会影响其他进程的正常运行,但是一个进程中的一个线程挂了,可能会影响到这个进程的正常运行!
     
  • 任意一个线程,都可以在创建一个新的线程。
     
  • 线程分为前台线程和后台线程,所有前台线程结束,整个进程才会结束,后台线程的结束,不会影响到进程的运行

注意:前台线程和后台线程除了是否能决定进程能否退出,在其他方面没有任何区别。

2、多线程的优点

首先,还是用刚才厨房和厨师的例子,假如说一个餐厅的客人非常的多,但是这么大的后厨,和这么多的需求只有一个厨师在卖命的炒菜,这显然是会被人骂的,餐厅早晚会倒闭。

为了充分利用厨房的各种资源,把每个厨具都用上,提高出餐效率,就可以请做个厨师同时进行工作。

既,充分利用进程所申请到的硬件资源(尤其是cup内核),多线程并发执行,高效完成任务,避免资源的浪费。

其次,虽然多进程也是可以实现并发编程的,但是进程的创建、调度、销毁速度远远慢与线程。既,线程更加轻量。

二、线程的创建方式

java中创建线程,首先要了解一个类(Thread)和一个函数式接口(Runnable)

他们都有一个run()方法(Runnable的runf方法是抽象的,Thread的run方法是具体的),想要创建线程,就必须重写这个run()方法,然后把对象赋值给Thread类,通过Thread类的start()方法创建线程。

方式1——继承Thread:

java">class MyThread extends Thread{@Overridepublic void run(){while(true){System.out.println("MuCreate");}}
}public class Threads {public static void main(String[] args) {MyThread t1=new MyThread();t1.start();while(true){System.out.println("主线程执行");}}
}

运行结果(截取部分)

以上是通过继承的方式。

Thread类有几个构造方法我们必须了解(等一下还会用到一个哦):

方式2——实现Runnable:

Runnable是一个函数式接口,只包含run这一个抽象方法(注意,Thread类中的run是一个具体的方法)

什么是函数式接口?

1、只包含一个抽象方法

2、可以有默认方法、静态方法(jdk8后引入)

3、和普通接口一样定义的变量必须初始化并且默认被public static final修饰

4、可以用@FunctionalInterface来检查其准确性

因此我们需要实现Runnable才能重写run方法:

java">class MyThread implements Runnable {//实现并重写run@Overridepublic void run() {while (true) {System.out.println("MuCreate");}}
}public class Threads {public static void main(String[] args) {/*下面两种创建方式都可以*///MyThread MyT=new MyThread();//Thread t1=new Thread(MyT);//把接口对消给到Thread的构造方法Thread t1 = new Thread(new MyThread());//把接口对消给到Thread的构造方法t1.start();while (true) {System.out.println("主线程执行");}}
}

运行结果这里不演示了,和Thread的一样。

注意:

1)对于main方法,系统会自动创建一个线程,这个线程叫主线程

2)只有在调用了Thread对象的start方法,才会创建线程,如果调用run方法,那么run方法里的程序实际上是在主线程上运行的。

3)通过start方法创建的线程默认是前台线程(所有前台线程结束JVM才会退出,同时不论后台线程有没有结束,后台线程也会自动退出)

除了以上两种创建线程的方式,下面还有三种创建线程的方法需要学习,都需要学习掌握。

不过不用怕,下面这三种方式本质上没有区别,都是用到的Thread的这个构造方法:

方式3——匿名内部类之Runnable:

本质就是方式2,既用一个类实现Runnable,不过方式3的这个类没有名字。

java">     Runnable runnable=new Runnable() {@Overridepublic void run() {System.out.println("我是手动创建的线程");}};

这段代码的含义是先创建一个匿名内部类,这个匿名内部类实现了Runnable接口,并且重写了run方法。然后把创建好的匿名内部类对象,重新赋值给了runnable,既向上转型。

然后再创建Thread类,调用刚才给的构造方法即可:

java">        Runnable runnable=new Runnable() {@Overridepublic void run() {System.out.println("我是手动创建的线程");}};Thread thread=new Thread(runnable);thread.start();

当然也可以在Thread的构造方法中直接进行匿名内部类的编写,省去runnable的创建:

java">  Thread thread = new Thread(new Runnable() {@Overridepublic void run() {while(true){System.out.println("我是手动创建的线程");}}});thread.start();

这样写的话代码的内聚性更强。
 

方式4——匿名内部类之Thread

本质就是方式1,既用一个类继承Thread,不过方式4的这个类没有名字。

java">     Thread thread = new Thread(){@Overridepublic void run(){while(true){System.out.println("创建的线程");}}};thread.start();while(true){System.out.println("主线程");}

方式5——匿名内部类之lambda

方式5的创建和方式3其实是一样的,不过这里换成了lambda表达式

通过lambda表达式重写run方法,并创建一个Runnable的对象,通过赋值的方式,传递给Thread

类的构造方法。

java">   Thread thread = new Thread(() -> {//下面直接编写方法体while (true) {System.out.println("hehe");}});thread.start();

除了这几种,实际上还可以使用Callable\Excuters创建,以后都会学习到,这里先做一个了解即可。


不论是那种创建方式,都需要重写run()方法,run()方法相当于线程入口。

并且都必须通过Thread类调用start()方法才能启动线程,如果调用run()方法是不会创建线程的,它会仍然在主线程中运行run()方法中的程序。

注意:

start()创建的线程,默认都是前台线程,如果不进行命名,默认按照Thread-0、Thread-1这种方式进行取名。



http://www.ppmy.cn/devtools/89005.html

相关文章

LeetCode 第136场双周赛个人题解

Q1. 求出胜利玩家的数目 原题链接 Q1. 求出胜利玩家的数目 思路分析 直接模拟 时间复杂度&#xff1a;O(N) AC代码 class Solution { public:int winningPlayerCount(int n, vector<vector<int>>& pick) {unordered_map<int, unordered_map<int, …

【Material-UI 组件】Autocomplete 中的 Grouped 功能详解

文章目录 一、组件概述1.1 Grouped 功能介绍1.2 适用场景 二、基础用法2.1 实现 Grouped 功能代码拆解 三、高级配置3.1 自定义组渲染3.2 常见配置 四、最佳实践4.1 数据排序4.2 组标题优化4.3 性能优化4.4 可访问性 五、总结 Grouped 功能使得 Autocomplete 组件能够按特定维度…

go range使用,及在赋值使用时候的陷阱点

for range中临时变量取值是列表元素的副本 代码代码一&#xff1a; studentInfos : [3]StudentInfo{}for _, a : range studentInfos {a.Name "zhangsan"a.Age 18a.Sex "man"}for _, stu : range studentInfos {fmt.Println("student info:"…

白骑士的PyCharm教学高级篇 3.4 服务器部署与配置

系列目录 上一篇&#xff1a;白骑士的PyCharm教学高级篇 3.3 Web开发支持 在开发完成后&#xff0c;将代码部署到服务器上是一个关键步骤。PyCharm不仅提供了强大的本地开发支持&#xff0c;还为远程服务器配置与部署、自动化部署流程提供了便捷的工具和功能。本文将详细介绍如…

HarmonyOS实现商品分类导航页面

目录 一:功能概述 二:代码实现 三:效果图 一:功能概述 分类导航采用左右结构布局,我们这里简单展示一级分类,以及该分类下的商品信息。左侧显示商品的一级分类,右侧显示显示该分类的商品。默认从首页进入该分类页面时,显示第一个分类的商品,切换一级分类时,传入分…

内联函数的概念和用途以及区别

内联函数&#xff08;Inline Function&#xff09;是C&#xff08;以及C99之后的C语言&#xff09;中的一个特性&#xff0c;旨在通过减少函数调用的开销来提高程序的执行效率。在正常情况下&#xff0c;当程序调用一个函数时&#xff0c;会发生一系列的操作&#xff0c;包括保…

《网络安全自学教程》- MySQL匿名用户的原理分析与实战研究

《网络安全自学教程》 低版本的MySQL数据库在安装时会创建一个用户名和密码为空的账户&#xff0c;也就是匿名账户。即使升级到高版本&#xff0c;匿名账户仍然会存在。 MySQL匿名账户 1、检查是否存在匿名账户2、检查用户权限3、创建匿名账户4、使用匿名账户登录5、删除匿名账…

三角形的四心的向量表示 | 难点

前言 若三角形的四心用文字语言表述时&#xff0c;许多学生还可以对付一阵&#xff0c;若但换成向量形式的符号语言&#xff0c;则大多就哑口无言了&#xff0c;所以有必要将三角形四心的向量表示形式好好作以总结储备。 相关延伸 常用结论 1、已知 O O O 为 △ A B C \tr…