专栏:JavaEE初阶起飞计划

个人主页:手握风云

目录

一、认识线程

1.1. 概念

1.2. 为什么要使用线程

1.3. 进程和线程的关系

1.4. 多线程模型

二、多线程的创建

2.1. 继承Thread类

2.2. 实现Runnable接口

2.3. 匿名内部类

2.4. lambda表达式


一、认识线程

1.1. 概念

        进程是操作系统进行资源分配和调度的基本单位。简单来说,进程就是程序的一次执行过程。通过进程,用户可以同时运行多个应用程序,提高计算机的利用率和用户体验。同时,进程间的隔离性也增强了系统的稳定性和安全性,一个进程的崩溃通常不会影响到其他进程。

1.2. 为什么要使用线程

        进程虽然说可以实现并发编程的效果,比如写一个服务器来同时处理多个进程,可以进程级别太高,创建或者销毁进程的开销很大。为了优化这些问题,于是引入了线程。

1.3. 进程和线程的关系

        进程包含线程,线程也可以被称为轻量级进程。每个线程都可以独立执行一段逻辑,并且独立在CPU上调度;同一个进程中的多个线程,共享进程的资源,如内存资源、文件描述符表等。当进程已经有了,在进程内部在创建新线程,就把申请资源开销省下来了。

1.4. 多线程模型

        我们假设有一个滑稽老铁去吃100只烧鸡,花费的时间肯定会太长。按照多进程模型,让两个滑稽老铁一人分别吃50只,虽然效率提升了,但我们需要额外申请内存空间。

        但我们如果按照多线程的模型来,让两个滑稽老铁在同一个桌子上来分别吃50只烧鸡,省下了申请资源的开销,效率就会进一步提高。

        但线程并不是越多,效率就越高。当滑稽老铁的数目进一步增多,但桌子就这么大,有可能其中一人吃得好好的,会被其他人挤走。此时非但不会提高效率,效率反而越低了,那此时就只能对机器进行性能优化。

        当线程数目比较少时,也会出现一些问题:

  • 当两个线程尝试操作一个共享的资源时,比如内存中的同一个变量,就可能会发生冲突,从而引起bug。
  • 如果某个线程抛出异常,且没有其他代码把这个异常catch住,就会导致进程内的所有线程会被随之带走,整个进程也会结束。

二、多线程的创建

        多线程编程和多进程编程都是操作系统提供的API,因为Java是一次编译到处运行的,JVM已经把系统差异给屏蔽了,JVM已经把多线程的API进行了较好地封装,但多进程的API封装得比较粗糙。下面就通过代码来感受一下Java中如何使用多线程。

2.1. 继承Thread类

public class Demo1 {
// 定义一个静态内部类MyThread,继承自Thread类static class MyThread extends Thread {// 重写run方法@Overridepublic void run() {System.out.println("hello thread");}}public static void main(String[] args) {// 创建一个MyThread对象Thread thread = new MyThread();// 启动线程thread.start();}
}

        Thread类是Java中的核心类,用于表示和管理线程。由于操作系统本身就提供了一组操作线程的函数,但这个函数是C语言版本的。JVM把这些操作系统的函数给封装成了Java版本,到了程序员手中,就成了Thread。使用Thread类,就相当于在使用操作系统的API。run方法是线程执行体,包含了线程需要执行的任务代码。当线程启动时,run方法中的代码将在新的线程中执行,也就是多线程程序的入口。

        在MyThread类里面重写的是run()方法,但在main()方法里调用的却是start()方法,这是因为start()方法是调用系统API,真正在操作系统内部创建一个线程,这个新的线程就会以run()作为入口,执行里面的逻辑,而run()方法就不需要在代码中显式调用。

        但如果我们调用run()方法,虽然也能打印"hello thread",但没有创建新的线程,而是直接在main()方法所在的 “主线程” 里执行了run()方法的逻辑。对于一个进程,至少得有一个线程;对于一个Java程序来说,main()方法,所在的进程至少会有这个线程,这个线程就是主线程。上面的代码就具有一个主线程和一个新创建的线程。

  • sleep静态方法

        Thread.sleep是Thread类中的一个静态方法。当调用这个方法时,当前线程会被阻塞,不执行任何操作,直到指定的毫秒数过去。这个方法可以用于多种场景,比如在循环中添加延迟,或者在执行某些操作之前等待一段时间。

public class MyThread extends Thread {@Overridepublic void run() {System.out.println("Hello Thread!");}
}
public class Demo2 {public static void main(String[] args) {// 创建一个MyThread对象Thread thread = new MyThread();// 启动线程thread.start();while (true) {System.out.println("Hello Main!");// 休眠1000秒Thread.sleep(1000);}}
}

        但此时的代码会显示出错误,原因是我们有受查的异常,必须进行显式处理,可以用throws抛出或者try{}catch{}进行捕获。

//throws
public class Demo2 {public static void main(String[] args) throws InterruptedException {// 创建一个MyThread对象Thread thread = new MyThread();// 启动线程thread.start();while (true) {System.out.println("Hello Main!");// 休眠1000秒Thread.sleep(1000);}}
}//try{}catch{}
public class Demo2 {public static void main(String[] args) {// 创建一个MyThread对象Thread thread = new MyThread();// 启动线程thread.start();while (true) {System.out.println("Hello Main!");// 休眠1000秒try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

        同理,我们在MyTread类里面进行循环打印。但这里只能使用try{}catch{}进行捕获,因为run()方法是重写自父类的方法,如果使用throws抛出不符合父类方法的声明。

        完整代码实现:

public class MyThread extends Thread {@Overridepublic void run() {while (true) {System.out.println("Hello Thread!");try {// 休眠1秒Thread.sleep(1000);} catch (InterruptedException e) {// 抛出运行时异常throw new RuntimeException(e);}}}
}
public class Demo2 {public static void main(String[] args) throws InterruptedException {// 创建一个MyThread对象Thread thread = new MyThread();// 启动线程thread.start();while (true) {System.out.println("Hello Main!");// 休眠1000秒Thread.sleep(1000);}}
}

        运行结果如下,虽然两个打印分别在不同的while循环中,两个线程属于并发关系,独立在CPU上执行。我们同时也可以看到两个线程不是严格交替执行的,由于两个线程都加了休眠,当1000毫秒到后,哪个线程线程的执行顺序是无法确定的,这是因为操作系统调度的线程是随机不可预测的。

        我们还有更直观的办法来观察多线程:在我们安装的JDK里面的bin目录下,以管理员身份运行jconsole.exe,然后点击连接本地进程Demo2,然后点击线程,就可以看到我们的Thread-0和main两个线程,剩下的线程都是JVM自带的,这些线程进行了一些背后的操作,比如垃圾回收、记录统计信息、记录一些调试信息等。

2.2. 实现Runnable接口

public class Demo3 {public static void main(String[] args) throws InterruptedException {// 创建一个实现了Runnable接口的实例Runnable myRunnable = new MyRunnable();// Runnable本身没有start方法,需要配合Thread类来使用// 创建一个Thread对象,并将myRunnable作为参数传入Thread thread = new Thread(myRunnable);// 启动线程thread.start();while (true) {System.out.println("Hello Main!");Thread.sleep(1000);}}
}class MyRunnable implements Runnable {@Overridepublic void run() {while (true) {System.out.println("Hello Tread!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

        运行结果:

        对于第一种继承Thread写法,描述任务的时候,代码是写到Thread子类中的,意味着任务内容与Thread类耦合度高,未来想把这个任务给别的主体来执行。

        对于第二种Runnable接口写法,任务是写到Runnable中,不涉及到任何和“线程”的概念,任务内容和Thread耦合度几乎没有,后序可以把这个任务交给进程或者协程来执行。

2.3. 匿名内部类

public class Demo4 {public static void main(String[] args) {// 创建一个新的线程Thread thread = new Thread() {@Overridepublic void run() {while (true) {System.out.println("Hello Tread!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}};// 启动线程thread.start();while (true) {// 在主线程中输出"Hello Main!"System.out.println("Hello Main!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

        此处创建了没有的名字匿名内部类,并且这个类是Thread类的子类,该子类也重写了run()方法,也创建了子类实例,通过thread引用指向子类。这样写可以简化代码。

        运行结果如下:

2.4. lambda表达式

public class Demo5 {public static void main(String[] args) {// 创建一个新的线程Thread thread = new Thread(() -> {while (true) {System.out.println("Hello Thread!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});// 启动线程thread.start();// 无限循环while (true) {System.out.println("Hello Main!");try {// 主线程休眠1秒Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

        运行结果如下:

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

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

相关文章

【StarRocks系列】建表优化

目录 一、数据模型选择 (核心优化) 二、分区与分桶策略 (数据分布优化) 三、字段类型与压缩 四、索引策略 五、高级特性应用 六、建表示例(关键优化整合) 参考官网 优化性能 | StarRocks 在 StarRocks 中创建表时,合理的表设计是性能优…

linux-vim编辑器

linux-vim编辑器 前言一、命令模式1. 跳转功能2. 文本编辑3. 模式切换 二、输入模式1. 进入输入模式2. 快捷键 三、末行模式1. 进入末行模式2. 文件操作3. 查找与替换4. 行操作 四、替换模式五、可视模式1. 进入可视模式2. 文本操作 六、相关配置 前言 vim - Vi IMproved, a p…

SQL关键字三分钟入门: 表结构管理与分区设计。(ALTER、MODIFY、CHANGE、DEFAULT、VALUES、LESS THAN、RANGE)

前面我们已经学习了如何查询数据(SELECT)、筛选数据(WHERE)等操作。现在我们要进入数据库的另一个重要领域 —— 表结构管理与分区设计。 本文带你快速认识以下关键字: ✅ ALTER✅ MODIFY✅ CHANGE✅ DEFAULT✅ VALU…

深度剖析:RTTI轻量框架实现原理与架构(C++ 17 高级编程)

🚀 C RTTI反射系统深度设计文档 🌌 核心架构图 #mermaid-svg-aWkaWoFklq1ylap6 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-aWkaWoFklq1ylap6 .error-icon{fill:#552222;}#mermaid-svg-a…

03-D3.js SVG text标签​

Data Visualization D3.js • SuperHiLearn how to create interactive, engaging experiences using HTML, CSS, SVG and Javascript.https://www.superhi.com/catalog/data-visualization-with-d3 text - SVG:可缩放矢量图形 | MDNtext元素定义了一个由文字组成…

Python 使用Gitlab Api

代码 REST API 见自带帮助文档 python 安装python-gitlab pip install --upgrade python-gitlab使用API 参考:https://python-gitlab.readthedocs.io/en/stable/api-usage.html import gitlab# anonymous read-only access for public resources (GitLab.com…

中医体质识别:理论、方法与应用的简要综述

中医体质识别:理论、方法与应用的简要综述 摘要 中医体质识别是中医“治未病”及个性化诊疗的关键环节。本文系统阐述中医体质识别,涵盖理论基础、常见体质类型、识别方法、现代技术应用及临床实践。中医体质理论源远流长,《黄帝内经》奠定…

稀疏表原理及应用场景

1 概述 稀疏表(Sparse Table,ST)是一种用于高效解决 静态区间查询(Range Query) 问题的数据结构,主要用于 可重复贡献问题(Idempotent Range Queries),例如区间最小值&a…

【深度学习与机器学习的区别】从本质到应用的全景对比

目录 前言 一、三者关系:深度学习是机器学习的子集 1.1 概念关系 1.2 类比理解:动物 vs 哺乳动物 1.3 举个例子更清楚 1.4 为什么“机器学习 ≠ 深度学习”? 1.5 最容易搞混的地方 二、核心区别总览(对比表) …

Masscan常用命令详解

一、工具介绍 Masscan是一款开源、高速的网络端口扫描工具,设计目标是实现最快的扫描速度。它能够在极短的时间内完成大规模的网络扫描,适用于互联网级别的扫描任务。它采用异步传输和自定义TCP/IP协议栈技术,最快可实现每秒160万数据包的扫…

STM32的内部RC与外部晶振电路

内部RC是“能用”,外部晶振是“用得准”。 一、STM32芯片内部的“晶振电路”是什么? STM32内部确实集成了两个RC(电阻-电容)振荡器: HSI(高速内部振荡器):通常8MHz,精…

为OneCode 开发TRea 开发插件,从环境搭建到生态融合

作为 AI 原生开发环境,TRea 的插件体系支持开发者基于其核心能力(如自然语言代码生成、AI 代码分析)进行功能扩展。本文以开发一个 "OneCode 组件生成插件" 为例,详解如何通过 TRea 开放接口实现自定义功能,…

Spring JDBC配置与讲解

目录 一、Spring JDBC概述1、Spring JDBC需要配置的依赖2、Spring配置项文件配置 二、Spring JDBC的使用1、Spring JDBC的增加操作2、Spring JDBC的修改操作3、Spring JDBC的删除操作4、Spring JDBC的查询操作 三、Spring JDBC的事务1、xml的形式进行事务2、Transactional注解 …

【AI智能体】Spring AI MCP 服务常用开发模式实战详解

目录 一、前言 二、MCP 介绍 2.1 MCP是什么 2.2 MCP 核心特点 2.3 Spring AI MCP 介绍 2.3.1 Spring AI MCP架构 2.3.2 Spring AI MCP分层说明 2.4 两种模式介绍 三、本地开发SSE模式 3.1 搭建mcp-server 3.1.1 导入工程核心依赖 3.1.2 添加配置文件 3.1.3 提供两个…

OpenStack 入门与实践

一、云计算概述 1.1 云计算的定义与本质 云计算(Cloud Computing)是一种基于网络的超级计算模式,它能够根据用户的不同需求,动态提供所需的计算资源、存储资源和网络资源等。这种模式就像我们日常生活中使用水电煤气一样&#x…

AntV L7入门教程

以下教程将系统地介绍 AntV L7 的核心 Scene 类用法,涵盖实例化、地图配置、视图操作、图层管理、事件监听及资源销毁等常用 API,并为每个方法给出完整示例代码。所有示例均基于官方 API 文档 ([l7.antv.antgroup.com][1])。 一、安装与引入 # 安装 L7…

【边缘计算】场景

工业互联网 对现场采集的数据进行数据预处理,将现场有用的信息提取出来实时上传给平台,为平台大大减轻了处理的工作量。 汇聚现场数据统一接口上传数据到云端,大大提高系统多样部署的安全性,解决现场数据跨域访问的问题。制造企业…

【FPGA学习】DDS信号发生器设计

目录 一、设计原理与准备​ 1.1 DDS 原理​ 1.2 IP 核学习与准备​:FPGA开发中常用IP核——ROM/RAM/FIFO 2、ROM文件的设置 1.3 开发环境搭建​ 二、DDS 信号发生器设计实现 2.1 系统架构设计​ 2.2 代码编写与模块实现​ 三、测试结果与总结​ 参考文献&…

pyqt 简单条码系统

生产数据管理系统说明 系统概述 这是一个基于PyQt5和pyodbc开发的生产数据管理系统,主要用于管理生产过程中的物料绑定和查询操作。系统提供了上料绑定和下料查询功能,支持与SQL Server数据库交互,实现数据的插入、查询、更新和删除操作。界…

【unitrix】 4.1 类型级加一操作(Add1.rs)

一、原码 这段代码实现了一个类型级的加一操作(Add1 trait),用于在Rust的类型系统中进行数值加一运算。 //! 加一操作特质实现 / Increment operation trait implementation //! //! 说明: //! 1. Z0、P1,、N1 1,常规计算 //! 2. …