JavaEE之多线程(创建线程的五种写法)详解

😽博主CSDN主页: 小源_😽

🖋️个人专栏: JavaEE

😀努力追逐大佬们的步伐~


目录

1. 前言 

2. 操作系统"内核"

3. 创建线程的五种写法 (我们重点要掌握最后一种写法!!)

3.1 继承 Thread, 重写 run

3. 2 实现 Runnable 接口, 重写 run

3.3 继承 Thread, 重写 run, 使用匿名内部类

3.4 实现 Runnable, 重写 run, 使用匿名内部类

3.5  [常用/推荐] 使用 lambda 表达式

4. 小结


1. 前言 

我们在写代码的时候, 可以使用多进程进行并发编程, 也可以使用多线程并发编程.

但是多进程并发编程在 Java 中不太推荐, 因为很多和多进程编程相关的 api 在 Java 标准库中都没有提供, 并且在上篇文章中我们讲解了多线程并发编程时效率更高(在需要频繁创建和销毁进程的时候), 并且对于 Java 进程, 需要启动 Java 虚拟机, 导致开销更大 (搞多个 Java 进程就是搞多个 Java 虚拟机)

系统提供了多线程编程的 api, Java 标准库中把这些 api 封装了, 在代码中可以直接使用. 我们重点学习 Thread 这样的类

本章重点

本文着重讲解了创建线程的五种写法


2. 操作系统"内核"

我们在学习创建线程之前, 需要先了解操作系统"内核", 它是操作系统中最核心的模块 (用来管理与硬件和给软件提供稳定的运行环境)

操作系统有两个状态: 内核态和用户态, 并且各有自己的空间 (内核空间, 用户空间)

比如我们平时运行的普通的应用程序 (如 idea, java, 画图板, qq音乐......) 都是运行在用户态的, 当操作这些从程序时, 不是应用程序直接操作的, 而是需要调用系统的 api, 在内核中完成操作

为什么要划分出这两个状态呢??

最主要的目的是为了 "稳定": 防止你的应用程序破坏硬件设备或者软件资源

系统封装了一些 api, 这些 api 都是一些合法的操作, 应用程序只能调用这些 api, 就不至于对系统火与硬件设备产生危害 (如果应用程序可以直接操作硬件, 极端情况下, 代码出现 bug, 可能把硬件烧坏)


3. 创建线程的五种写法 (我们重点要掌握最后一种写法!!)

每个线程都是一个独立的执行流, 每个线程都能够独立的去 cpu 上调度执行

3.1 继承 Thread, 重写 run

  1. 创建一个自己的类, 继承自这个 Thread
  2. 根据刚才的类, 创建出实例
  3. 调用 Thread 的 start 方法 
package thread;// 1. 创建一个自己的类, 继承自这个 Thread// 这个 Thread 类能直接使用, 不需要导入包, 是因为 Java 标准库中, 有一个特殊的包 java.long, 和 String 类似 (也在 java.long 包中)
class MyThread extends Thread {// 这里重写的 run 入口方法必须手动指定, 针对原有的 Thread 进行扩展 (把一些能复用的复用, 需要扩展的扩展)@Overridepublic void run() {// run 方法就是该线程的入口方法. 和 main 方法类似, main 方法是一个进程的入口方法 (也可以说 main 方法是主线程的入口方法)// 一个进程至少有一个线程, 这个进程中的第一个线程就叫做"主线程", 如果一个进程只有一个线程, 即 main 线程就是主线程System.out.println("hello world");}
}public class ThreadDemo1 {public static void main(String[] args) {// 2. 根据刚才的类, 创建出实例. (线程实例,才是真正的线程).MyThread t = new MyThread();// Thread t = new MyThread();// 3. 调用 Thread 的 start 方法, 才会真正调用系统的 api, 在系统内核中创建出线程, 然后线程就会执行上面的 run 方法了t.start();}
}

按照之前的理解 (没有学习多线程之前), 如果一个代码出现了死循环, 最多只能执行一个, 另一个循环是进不去的, 下面我们来创建两个线程

package thread;class MyThread2 extends Thread {@Overridepublic void run() {while (true) {System.out.println("hello thread");// (我们使用的 sleep 是 Java 中封装后的版本, 是 Thread 提供的静态方法) 加 sleep 来降低循环的速度 (这里让 t 线程睡眠 1s (1000ms 等于 1s)), 先写第 19 行代码把鼠标指针放在 sleep 上, 按 Alt + Enter 即可try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}public class ThreadDemo2 {public static void main(String[] args) {Thread t = new MyThread2();t.start();while (true) {System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

两个线程都在执行, 互不干扰, 运行结果为: 

我们可以发现:  每一秒打印的顺序都是随机的, 这里涉及到一个重要结论, 当一个进程中有多个线程时, 这些线程执行的先后顺序, 是完全随机的. (因为操作系统内核中, 有一个"调度器" 模块, 这个模块的实现方式类似 "随机调度" 的效果)

什么是"随机调度"

  1. 一个进程什么时候被调度到 cpu 上执行的时机是不确定的
  2. 一个线程什么时候从 cpu 上下来, 给别人让位的时机也是不确定的

这是主流操作系统"抢占式执行"的体现, 但是给我们的多线程的安全问题埋下了伏笔

刚才我们只是通过打印的方式看到了两个执行流, 我们也可以使用一些第三方工具更直观地看到多个线程的情况

在 jdk 中, 有一个叫 jconsole 的工具 

选择本地进程中我们刚刚执行的 ThreadDemo2 代码, 然后直接连接即可, 

直接选择不安全的连接即可

线程是在正在不停的运行的, 当我们点击 Thread-0 线程的详细情况的一瞬间, 相当于"咔嚓"一个快照把这一瞬间的 Thread-0 线程的状态展示出来了 (再次点击时, 线程的详细情况可能会改变)

这里的"堆栈跟踪", 就是线程的调用栈, 描述了线程当前执行到哪个方法的第几行代码, 以及这个方法是如何一层一层调用过去的

除了 main 线程和 t 线程, 其余的线程都是 JVM 自带的线程, 完成一些垃圾回收, 以及监控统计各种指标 (如果我们的代码出现问题, 就可以从中提取一些参考和线索), 把统计指标通过网络的方式, 传输给其他程序,

 


3. 2 实现 Runnable 接口, 重写 run

只是实现接口时改变, 其余和上面的代码类似

package thread;// Runnable 可理解为 "可执行的", 通过这个接口, 就可以抽象出一段可以被其他实体执行的代码
class MyThread3 implements Runnable {@Overridepublic void run() {while (true) {System.out.println("hello runnable");//睡眠 1stry {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}public class ThreadDemo3 {public static void main(String[] args) {Thread t = new Thread(new MyThread3());t.start();while (true) {System.out.println("hello main");//睡眠 1stry {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

3.3 继承 Thread, 重写 run, 使用匿名内部类

内部类是在一个类里面定义的类, 最多使用的就是匿名内部类 (这个类没有名字, 不能重复使用, "用一次就扔掉")

package thread;public class ThreadDemo4 {public static void main(String[] args) {// 写 { 是因为要定义一个新的类, 继承自 Thread, {} 中定义子类的属性和方法, 此处最主要的目的是重写 run 方法// t 指向的实例不是单纯的 Thread, 而是新定义的匿名内部类 (Thread 的子类)Thread t = new Thread() {@Overridepublic void run() {while (true) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}};t.start();while (true) {System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

3.4 实现 Runnable, 重写 run, 使用匿名内部类

package thread;public class ThreadDemo5 {public static void main(String[] args) {Thread t = new Thread(new Runnable() {// Thread 构造的方法的参数, 填写了 Runnable 的匿名内部类的实例@Overridepublic void run() {while (true) {System.out.println("hello runnable");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}});t.start();while (true) {System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

3.5  [常用/推荐] 使用 lambda 表达式

这是最简洁的写法

lambda 主流语言都有: c++, Python 中叫做 lambda, JS, GO 直接叫做匿名函数

因为方法不能脱离类单独存在, 所以导致上面几种方法为了设置回调函数 而套上了一层类

因此引入了 lambda 表达式 (就是一个匿名函数/方法), Java语法首创, 函数式接口属于 lambda 背后的实现, 相当于在没有破坏原有规则 (方法不能脱离类单独存在) 的基础上, 给了lambda 一个合理的解释

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

4. 小结

上述的5种写法都是等价的, 可以互相转换的, 用 lambda 表达式是我们最常用, 最推荐, 最简洁的写法


最后,祝大家天天开心,更上一层楼!关注我🌹,我会持续更新学习分享...🖋️

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

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

相关文章

电脑坏了去维修,第一家报价800,第三家说报废!

这篇文章主要讲的是修理坏掉的电脑。 第一家报价300,第二家报价800,第三家说要报废! 相信很多朋友对于修电脑坏了要多少钱有很多困惑,修电脑坏了要多少钱,到底去正规售后服务还是去非品牌店维修一台坏掉的电脑。 今天高…

Jmeter扩展---自定义取样器

简介 Jmeter已经内置了各种协议的取样器,已经能满足常用的性能压测需求。且在前面一章Jmeter扩展开发--自定义java取样器-CSDN博客中也有关于Java取样器的扩展开发,不过有时候我们期望能定制自己的取样器和界面。为此,需要对Jmeter做扩展&am…

界面控件DevExpress ASP.NET Scheduler - 助力快速交付个人信息管理系统(下)

DevExpress ASP. NET Scheduler组件能完全复制Microsoft Outlook Scheduler的样式和功能,具有日、周、月和时间轴视图,并包括内置的打印支持,因此用户可以在尽可能短的时间内交付全功能的个人信息管理系统。在上文中(点击这里回顾…

Gemma中RoPE代码详细讲解

最近在看Gemma代码感觉比LLama的代码看的方便点, 看到RoPE代码跟常规的方式不太一样(也不算常规,就是我理解的方式),特此记录一下。我的RoPE入门代码参考:Rotary Position Embedding (RoPE, 旋转式位置编码…

自然语言处理实验2 字符级RNN分类实验

实验2 字符级RNN分类实验 必做题: (1)数据准备:academy_titles.txt为“考硕考博”板块的帖子标题,job_titles.txt为“招聘信息”板块的帖子标题,将上述两个txt进行划分,其中训练集为70%&#xf…

服务器Debian 12.x中安装Jupyer并配置远程访问

服务器系统:Debian 12.x;IP地址:10.100.2.138 客户端:Windows 10;IP地址:10.100.2.38 利用ssh登录服务器: 1.安装python3 #apt install python3 2.安装pip #apt install python3-pip … 3.安装virtualen…

Unity Timeline学习笔记(3) - SignalTrack信号轨道和自定义带参数的Marker信号和轨道

信号轨道,顾名思义就是运行到某处发送一个信号。 普通用法 普通用法就是没有任何封装的,个人感觉特别难用,但是有必要理解一下工作原理。 添加信号 我们添加一个信号资源 生成后可以看到资源文件,这个是可以拖到SignalTrack上…

【Python数据结构与判断7/7】数据结构小结

目录 序言 整体回忆 定义方式 访问元素 访问单个元素 访问多个与元素 修改元素 添加元素 列表里添加元素 字典里添加元素 删除元素 in运算符 实战案例 总结 序言 今天将对前面学过的三种数据结构:元组(tuple)、列表(…

微前端框架 qiankun 配置使用【基于 vue/react脚手架创建项目 】

qiankun官方文档:qiankun - qiankun 一、创建主应用: 这里以 vue 为主应用,vue版本:2.x // 全局安装vue脚手架 npm install -g vue/clivue create main-app 省略 vue 创建项目过程,若不会可以自行百度查阅教程 …

java垃圾回收-三色标记法

三色标记法 引言什么是三色标记法白色灰色黑色 三色标记过程三色标记带来的问题多标问题漏标问题 如何弥补漏标问题增量更新原始快照总结 引言 在CMS,G1这种并发的垃圾收集器收集对象时,假如一个对象A被GC线程标记为不可达对象,但是用户线程又把A对象做…

数字化经济的前沿:深入了解 Web3 的商业模式

随着区块链技术的迅速发展,Web3作为一种新型的互联网范式,正逐渐引起人们的关注。它不仅仅是一种技术革新,更是一种商业模式和价值观的转变。本文将深入探讨Web3的商业模式,以及它对数字化经济的影响。 1. 理解Web3的商业模式 We…

算法---滑动窗口练习-4(无重复字符的最长子串)

无重复字符的最长子串 1. 题目解析2. 讲解算法原理3. 编写代码 1. 题目解析 题目地址:点这里 2. 讲解算法原理 算法的主要思想是使用滑动窗口来维护一个不含重复字符的子串。定义两个指针 left 和 right 分别表示窗口的左边界和右边界。还定义了一个数组 hash 来记…

Apache Paimon 的 CDC Ingestion 概述

CDC Ingestion 1)概述 Paimon支持schema evolution将数据插入到Paimon表中,添加的列将实时同步到Paimon表,并且无需重启同步作业。 目前支持的同步方式如下: MySQL Synchronizing Table: 将MySQL中的一个或多个表同步到一个Pa…

【算法与数据结构】深入解析二叉树(一)

文章目录 📝数概念及结构🌠 树的概念🌉树的表示🌠 树在实际中的运用(表示文件系统的目录树结构) 🌉二叉树概念及结构🌠概念🌉数据结构中的二叉树🌠特殊的二叉…

Spring web MVC(2)

1、RequestMapping称为路由映射(既是类注解也是方法注解提供访问路径) 2、RequestParam起到重命名的作用,也起到绑定的作用,传递集合list时会用到,多个值绑定给list,默认是必传参数如果不传参数需要设置re…

如何在Windows 10上打开和关闭平板模式?这里提供详细步骤

前言 默认情况下,当你将可翻转PC重新配置为平板模式时,Windows 10会自动切换到平板模式。如果你希望手动打开或关闭平板模式,有几种方法可以实现。​ 自动平板模式在Windows 10上如何工作 如果你使用的是二合一可翻转笔记本电脑&#xff0…

Spring, SpringBoot, SpringCloud,微服务

1,SSM (Spring+SpringMVC+MyBatis) SSM框架集由Spring、MyBatis两个开源框架整合而成(SpringMVC是Spring中的部分内容),常作为数据源较简单的web项目的框架。 Spring MVC 是 Spring 提供的一个基于 MVC 设计模式的轻量级 Web 开发框架,本质上相当于 Servlet,Controlle…

vue 基于elementUI/antd-vue, h函数实现message中嵌套链接跳转到指定路由 (h函数点击事件的写法)

效果如图: 点击message 组件中的 工单管理, 跳转到工单管理页面。 以下是基于vue3 antd-vue 代码如下: import { message } from ant-design-vue; import { h, reactive, ref, watch } from vue; import { useRouter } from vue-router; c…

PY32离线烧录器功能介绍,可批量烧录,支持PY32系列多款单片机

PY32离线烧录器可以对PY系列单片机进行批量烧录,现支持PY32F002A/002B/002/003/030/071/072/040/403/303芯片各封装和XL2409,XL32F001/003等芯片。PY32离线烧录器需要搭配上位机软件才能使用,上位机软件在我们官网(www.xinlinggo.…

【软考】UML中的图之对象图

目录 1. 说明2. 图示3. 特性 1. 说明 1.对象图即object diagram2.展现了某一时刻一组对象以及它们之间的关系3.描述了在类图中所建立的事物的实例的静态快照4.对象图一般包括对象和链5.对象图展示的是对象之间关系,不存在交互,所以不是交互图 2. 图示 …