【JavaEE初阶】应是天仙狂醉,乱把白云揉碎 - (重点)线程

在这里插入图片描述

本篇博客给大家带来的是线程的知识点, 由于内容较多分几天来写.
🐎文章专栏: JavaEE初阶
🚀若有问题 评论区见
⭐欢迎大家点赞 评论 收藏 分享 ❤❤❤
如果你不知道分享给谁,那就分享给薯条.
你们的支持是我不断创作的动力 .

1. 认识线程

1.1 概念

)1 线程是什么

⼀个线程就是⼀个 “执行流”. 每个线程之间都可以按照顺序执行自己的代码. 多个线程之间 “同时” 执行着多份代码.

⼀家公司要去银⾏办理业务,既要进⾏财务转账,⼜要进⾏福利发放,还得进⾏缴社保。
如果只有张三⼀个会计就会忙不过来,耗费的时间特别⻓。为了让业务更快的办理好,张三⼜找来两位同事李四、王五⼀起来帮助他,三个⼈分别负责⼀个事情,分别申请⼀个号码进⾏排队,⾃此就有了三个执⾏流共同完成任务,但本质上他们都是为了办理⼀家公司的业务。
此时,我们就把这种情况称为多线程,将⼀个⼤任务分解成不同⼩任务,交给不同执⾏流就分别排队执⾏。其中李四、王五都是张三叫来的,所以张三⼀般被称为主线程(Main Thread)。

)2 为什么要有线程

⾸先, “并发编程” 成为 “刚需(必备)”.

• 单核 CPU 的发展遇到了瓶颈. 要想提⾼算⼒, 就需要多核 CPU. ⽽并发编程能更充分利⽤多核 CPU资源.
• 有些任务场景需要 “等待 IO”, 为了让等待 IO 的时间能够去做⼀些其他的⼯作, 也需要⽤到并发编程.

其次, 虽然多进程也能实现并发编程, 但是线程比进程更轻量.

• 创建线程比创建进程更快.
• 销毁线程比销毁进程更快.
• 调度线程比调度进程更快.
总结就是线程创建更快,销毁更快,调度更快.

最后, 线程虽然比进程轻量, 但是⼈们还不满足, 于是又有了 “线程池”(ThreadPool) 和 (Coroutine)“协程”.(这两个后续的文章再说).

)3 进程和线程的区别(经典面试题)

• 1. 进程是包含线程的. 每个进程⾄少有⼀个线程存在,即主线程.
• 2. 进程和进程之间不共享内存空间. 同⼀个进程的线程之间共享同⼀个内存空间.

比如上面的多进程例子中,每个客户来银行办理各自的业务,但他们之间的票据肯定是不想让别人知道的,否则钱不就被其他⼈取走了么。而上面我们的公司业务中,张三、李四、王五虽然是不同的执行流,但因为办理的都是⼀家公司的业务,所以票据是共享着的。这个就是多线程和多进程的最大区别。

• 3. 进程是系统分配资源的最小单位,线程是系统调度的最小单位.
• 4. ⼀个进程挂了⼀般不会影响到其他进程. 但是⼀个线程挂了, 可能把同进程内的其他线程⼀起带走(整个进程崩溃).
• 5. 都是用来实现并发编程场景的. 但是线程比进程更轻量, 更高效.

在这里插入图片描述

)4 Java的线程 和 操作系统线程 的关系

线程是操作系统中的概念. 操作系统内核实现了线程这样的机制, 并且对用户层提供了⼀些 API 供用户
使用(例如 Linux 的 pthread 库).
Java 标准库中 Thread 类可以视为是对操作系统提供的 API 进行了进⼀步的抽象和封装.

1.2 创建线程的方式(面试题)

⽅法1 继承 Thread 类

一. 继承 Thread重写 run()方法 来创建⼀个线程类.

 class MyThread extends Thread {@Overridepublic void run() {//这个方法就是线程的入口方法while(true) {System.out.println("hello thread");try {//sleep() 让线程休眠 x ms(毫秒).Thread.sleep(1000);//此处抛异常只能 try catch 不能 throws 因为父类Thread没有throws, 子类也不行} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

二.创建 MyThread 类的实例

Thread t = new MyThread(); //向上转型

三. 调用 start 方法启动线程

t.start();
//创建线程的第一种写法
//创建一个类, 继承自 Threadclass MyThread extends Thread {@Overridepublic void run() {//这个方法就是线程的入口方法while(true) {System.out.println("hello thread");try {Thread.sleep(1000);//此处抛异常只能 try catch 不能 throws 因为父类Thread没有throws, 子类也不行} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}//创建线程
public class Demo1 {public static void main(String[] args) throws InterruptedException {Thread t = new MyThread(); //Java生态更鼓励向上转型// 封装本质上是让调用者不用了解类实现的细节降低了学习和使用的成本.
//多态则是在封装的基础上更进一步,多态则是都不需要你知道当前是啥类。//start 和 run 都是线程的入口(线程要做什么)//run只是描述了线程的入口 (线程要做什么任务)//start 则是真正调用了系统API, 在系统中创建出线程, 让线程再调用 run.t.start();//t.run();while(true) {System.out.println("hello main");Thread.sleep(1000); //自定义类 两种抛异常方式都可以.}}
}

方法2. 实现 Runnable 接⼝

一. 实现 Runnable 接口,重写run()方法.

class MyRunnable implements Runnable {@Overridepublic void run() {while(true) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

二. 创建 Thread 类实例, 调⽤ Thread 的构造⽅法时将 Runnable 对象作为 target 参数.

Runnable runnable = new MyRunnable();

三. 调用 start 方法

//创建线程的第二种写法//实现Runnable接口,重写run方法.//使用 Runnable 的写法,和 直接继承 Thread 之间的区别, 主要就是三个字,解耦合
class MyRunnable implements Runnable {@Overridepublic void run() {while(true) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}public class Demo2 {public static void main(String[] args) {Runnable runnable = new MyRunnable();Thread t = new Thread(runnable);t.start();while(true) {System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
}

方法3. 匿名内部类创建 Thread ⼦类对象

public class Demo3 {public static void main(String[] args) {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);}}}
}

方法4. 匿名内部类创建 Runnable ⼦类对象

public class Demo4 {public static void main(String[] args) {Thread t = new Thread(new Runnable(){@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);}}}
}

方法5. lambda 表达式创建 Runnable ⼦类对象(常用)

public class Demo5 {public static void main(String[] args) {Thread t = new Thread(() -> {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);}}}
}

2. Thread 类及常见方法

Thread 类是 JVM ⽤来管理线程的⼀个类,换句话说,每个线程都有⼀个唯⼀的 Thread 对象与之关联。

每个执行流,需要有⼀个对象来描述,类似下图所示,而 Thread 类的对象就是用来描述⼀个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理。

在这里插入图片描述

2.1 Thread 的常见构造方法


在这里插入图片描述

1 Thread t1 = new Thread();
2 Thread t2 = new Thread(new MyRunnable());
3 Thread t3 = new Thread("线程1");
4 Thread t4 = new Thread(new MyRunnable(), "线程2")

2.2 Thread 的几个常见属性

在这里插入图片描述
1. ID 是线程的唯⼀标识,不同线程不会重复.(此处ID是Java给线程分配的,不是系统API提供的线程ID,也不是PCB中的ID.)

2. 名称是各种调试工具用到.

3. 状态表示线程当前所处的⼀个情况.

4. 优先级高的线程理论上来说更容易被调度到.

5. 关于后台线程(守护线程),需要记住⼀点:JVM会在⼀个进程的所有非后台线程结束后,才会结束运行。

线程默认情况下是前台线程.
一个Java进程中,如果前台线程没有执行结束,整个进程都不会结束. 相比之下后台线程是否结束不影响整个进程的结束.

//是否后台线程, isDaemon() true变 后台,//线程默认是前台线程.
public class Demo6 {public static void main(String[] args) {Thread t = new Thread(() -> {while(true) {System.out.println("hello Thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}, "这是新线程");//设置 t 为后台线程t.setDaemon(true);t.start();}
}

6. 是否存活,即简单的理解,为 run 方法是否运行结束了

//isAlive() 方法的用法.
public class Demo7 {public static void main(String[] args) {Thread t = new Thread(() -> {System.out.println("线程开始");try {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("线程结束");});t.start();System.out.println(t.isAlive());try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(t.isAlive() );}
}

在这里插入图片描述

两线程是"并发执行"的为什么是先打印 true 而不是: "线程开始"呢?
系统的调度顺序不确定, 但是大概率是先打印true, 因为调用了start方法之后, 新的线程被创建也是有一定的开销的, 创建线程的过程中, 主线程就执行了. 当然也存在先打印 "线程开始"的情况. 比如: 主线程刚好卡了一会.

7. 线程的中断问题

public class ThreadDemo {public static void main(String[] args) {Thread thread = new Thread(() -> {for (int i = 0; i < 10; i++) {try {System.out.println(Thread.currentThread().getName() + ": 我还活着");Thread.sleep(1 * 1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() + ": 我即将死去");});System.out.println(Thread.currentThread().getName()+ ": ID: " + thread.getId());System.out.println(Thread.currentThread().getName()+ ": 名称: " + thread.getName());System.out.println(Thread.currentThread().getName()+ ": 状态: " + thread.getState());System.out.println(Thread.currentThread().getName()+ ": 优先级: " + thread.getPriority());System.out.println(Thread.currentThread().getName()+ ": 后台线程: " + thread.isDaemon());System.out.println(Thread.currentThread().getName()+ ": 活着: " + thread.isAlive());System.out.println(Thread.currentThread().getName()+ ": 被中断: " + thread.isInterrupted());thread.start();while (thread.isAlive()) {}System.out.println(Thread.currentThread().getName()+ ": 状态: " + thread.getState());}
}

在这里插入图片描述

2.3 启动⼀个线程 - start()

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

• 覆写 run 方法是提供给线程要做的事情的指令清单.
• 线程对象可以认为是把 李四、王五叫过来了.
• 而调用 start() 方法,就是喊⼀声:”行动起来!“,线程才真正独立去执行了.

2.3.1 start方法 和 run方法 的区别(面试题)

start方法内部会调用到系统API,来在系统内核中创建出线程.(调⽤ start ⽅法, 才真的在操作系统的底层创建出⼀个线程)
run方法就只是单纯地描述了该线程要执行什么样的内容.

2.4 中断一个线程

李四⼀旦进到工作状态,他就会按照行动指南上的步骤去进行工作,不完成是不会结束的。但有时我们需要增加⼀些机制,例如老板突然来电话了,说转账的对方是个骗子,需要赶紧停止转账,那张三该如何通知李四停⽌呢?这就涉及到我们的停止线程的方式了。

方法一. 通过共享的标记来进⾏沟通

//线程的打断//第一个方案 手动创建标志位
public class Demo8 {private static boolean isQuit = false;//此处isQuit不能定义到 main方法中,// 因为lambda表达式有一个语法规则变量捕获,可以自动的捕获到上层作用域中的局部变量.//实际上变量捕获还有一个前提就是 只能捕获final或者捕获一个final的变量(不改变的量)// 那此处也没有final修饰isQuit, 为什么lambda能访问到isQuit?//原来当isQuit是成员变量的时候, lambda访问它的时候就不是用变量捕获的语法了,//而是"内部类访问外部类属性", 此时就没有final的属性限制了.public static void main(String[] args) {Thread t = new Thread(() -> {// 此处的打印可以替换成任意的逻辑来表示线程的实际工作内容while(!isQuit) {System.out.println("线程工作中");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("线程工作完毕!");});t.start();try {Thread.sleep(5000);} catch (InterruptedException e) {throw new RuntimeException(e);}isQuit = true;System.out.println("设置 isQuit 为 true");}
}

方法二. 调⽤ interrupt() ⽅法来通知

使⽤ Thread.interrupted() 或者
Thread.currentThread().isInterrupted() 代替⾃定义标志位.
Thread 内部包含了⼀个 boolean 类型的变量作为线程是否被中断的标记

在这里插入图片描述

//线程终止//第二个方案 使用Thread类内部现有的一个标志位
public class Demo9 {public static void main(String[] args) {Thread t = new Thread(() -> {//Thread 类内部, 有一个现成的标志位,可以用来判定当前的循环是否要结束while(!Thread.currentThread().isInterrupted()) {System.out.println("线程工作中");try {Thread.sleep(1000);} catch (InterruptedException e) {//1. 假装没听见, 循环继续正常执行e.printStackTrace();/* throw new RuntimeException(e);*///2. 加上一个 break, 表示让线程立即结束//break;//3. 做一些其他工作, 完成之后再结束//其他工作的代码放到这里break;}}});t.start();try {Thread.sleep(5000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(" 让 t 线程终止");t.interrupt();}
}

thread 收到通知的方式有两种:

1. 如果线程因为调⽤ wait/join/sleep 等⽅法⽽阻塞挂起,则以 InterruptedException 异常的形式通知,清除中断标志。 当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择忽略这个异常, 也可以跳出循环结束线程.
2. 否则,只是内部的⼀个中断标志被设置,thread 可以通过
。 Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志.
这种⽅式通知收到的更及时,即使线程正在 sleep 也可以马上收到。

本篇博客到这里也就结束啦, 感谢你的观看!! ❤❤❤

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

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

相关文章

精准用户获取与私域流量运营:多商户链动 2+1 模式商城小程序的赋能策略

摘要&#xff1a;本文聚焦于精准用户对商业运营的核心价值&#xff0c;深入剖析获取精准用户的有效途径&#xff0c;特别围绕目标用户画像及出没场景展开分析。同时&#xff0c;探讨在私域流量构建进程中&#xff0c;多商户链动 21 模式商城小程序如何融入精准用户运营体系&…

Spring Boot教程之十一:获取Request 请求 和 Put请求

如何在 Spring Boot 中获取Request Body&#xff1f; Java 语言是所有编程语言中最流行的语言之一。使用 Java 编程语言有几个优点&#xff0c;无论是出于安全目的还是构建大型分发项目。使用 Java 的优点之一是 Java 试图借助类、继承、多态等概念将语言中的每个概念与现实世…

DVWA靶场文件包含(File Inclusion)通关教程(high级别)

目录 DVWA 靶场建立闯关 DVWA 靶场建立 需要的东西&#xff1a; phpStudy&#xff1a; 链接&#xff1a; phpStudy 提取码&#xff1a;0278 DVWA-master 链接&#xff1a; DVWA靶场 提取码&#xff1a;0278 建议在虚拟机中操作&#xff0c;以防数据库冲突&#xff0c;下面有…

基于yolov8、yolov5的铝材缺陷检测识别系统(含UI界面、训练好的模型、Python代码、数据集)

摘要&#xff1a;铝材缺陷检测在现代工业生产和质量管理中具有重要意义&#xff0c;不仅能帮助企业实时监控铝材质量&#xff0c;还为智能化生产系统提供了可靠的数据支撑。本文介绍了一款基于YOLOv8、YOLOv5等深度学习框架的铝材缺陷检测模型&#xff0c;该模型使用了大量包含…

力扣刷题TOP101:8.BM10 两个链表的第一个公共结点

目录&#xff1a; 目的 思路 复杂度 记忆秘诀 python代码 目的 两个无环的单向链表&#xff0c;它们的第一个公共结点{{6,7}。 思路 这个任务是找到两个链表的第一个公共结点。可以看作两个心机boy偷偷补课翻车事件。平时嘴上说自己在家玩游戏&#xff0c;实际上背地里都偷…

哪些行业对六西格玛管理方法的需求较大?

六西格玛作为一种追求极致质量和流程优化的管理哲学&#xff0c;自诞生以来&#xff0c;便在多个行业中展现出了巨大的应用价值。该方法通过定义、测量、分析、改进和控制&#xff08;DMAIC&#xff09;五个阶段&#xff0c;帮助企业实现流程的持续改进&#xff0c;提高产品质量…

Spring Web MVC其他扩展(详解下)

文章目录 Spring MVC其他扩展&#xff08;下&#xff09;异常处理异常处理机制声明式异常好处基于注解异常声明异常处理 拦截器拦截器概念拦截器使用拦截器作用位置图解拦截器案例拦截器工作原理源码 参数校验校验概述操作演示SpringMVC自定义参数验证ValueObject(VO) 文件上传…

排序学习整理(1)

1.排序的概念及运用 1.1概念 排序&#xff1a;所谓排序&#xff0c;就是使⼀串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作&#xff0c;以便更容易查找、组织或分析数据。 1.2运用 购物筛选排序 院校排名 1.3常见排序算法 2.实…

【linux学习指南】Linux进程信号产生(三) 硬件异常除零出错?野指针异常?core文件

文章目录 &#x1f4dd;前言&#x1f320;模拟除0&#x1f309;除0出错&#xff1f;&#x1f309;野指针异常? &#x1f320;⼦进程退出coredump&#x1f309;Core Dump &#x1f6a9;总结 &#x1f4dd;前言 硬件异常被硬件以某种⽅式被硬件检测到并通知内核,然后内核向当前…

【人工智能-科普】图神经网络(GNN):与传统神经网络的区别与优势

文章目录 图神经网络(GNN):与传统神经网络的区别与优势什么是图神经网络?图的基本概念GNN的工作原理GNN与传统神经网络的不同1. 数据结构的不同2. 信息传递方式的不同3. 模型的可扩展性4. 局部与全局信息的结合GNN的应用领域总结图神经网络(GNN):与传统神经网络的区别与…

青藤云安全携手财信证券,入选金融科技创新应用优秀案例

11月29日&#xff0c;由中国信息通信研究院主办的第四届“金信通”金融科技创新应用案例评选结果正式发布。财信证券与青藤云安全联合提交的“基于RASP技术的API及数据链路安全治理项目”以其卓越的创新性和先进性&#xff0c;成功入选金融科技创新应用优秀案例。 据悉&#x…

Python系列 - MQTT协议

Python系列 - MQTT协议 资源连接 MQTT的介绍和应用场景的示例说明 一、什么是MQTT 百度关于MQTT的介绍如下&#xff1a; MQTT(消息队列遥测传输)是ISO 标准(ISO/IEC PRF 20922)下基于发布订阅范式的消息协议。它工作在 TCP/IP协议之上&#xff0c;是为硬件性能低下的远程设…

winform跨线程更新界面

1、报错代码 下面的代码中的this.Text指的是一个winform的窗体&#xff0c;开启Task执行下面的代码以后直接报错&#xff0c;提示线程间操作无效&#xff0c;这是因为在WinForms应用程序中&#xff0c;UI元素&#xff08;如控件&#xff09;通常只能在创建它们的线程&#xff…

Mybatis:CRUD数据操作之多条件查询及动态SQL

Mybatis基础环境准备请看&#xff1a;Mybatis基础环境准备 本篇讲解Mybati数据CRUD数据操作之多条件查询 1&#xff0c;编写接口方法 在 com.itheima.mapper 包写创建名为 BrandMapper 的接口。在 BrandMapper 接口中定义多条件查询的方法。 而该功能有三个参数&#xff0c;…

音视频技术扫盲之预测编码的基本原理探究

预测编码是一种数据压缩技术&#xff0c;广泛应用于图像、视频和音频编码等领域。其基本原理是利用数据的相关性&#xff0c;通过对当前数据的预测和实际值与预测值之间的差值进行编码&#xff0c;从而实现数据压缩的目的。 一、预测编码的基本概念 预测编码主要包括预测器和…

5. langgraph实现高级RAG (Adaptive RAG)

1. 数据准备 from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.document_loaders import WebBaseLoader from langchain_community.vectorstores import Chromaurls ["https://lilianweng.github.io/posts/2023-06-23-age…

自动化配置

自动化配置共享目录 nfs&#xff1a;共享某个目录&#xff0c;共享给哪些客户端&#xff0c;rw&#xff08;读写&#xff09;——rwx&#xff08;给目录权限设置&#xff09;&#xff0c;ro&#xff08;只读&#xff09; 写脚本 1、装包 可以调用仓库之前装包的脚本 &#x…

AtomicIntegerFieldUpdater能否降低内存

1. 代码如下&#xff1a; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerTest {final AtomicInteger startPosition new AtomicInteger(0);final AtomicInteger wrotePosition new Atom…

微服务即时通讯系统的实现(服务端)----(3)

目录 1. 消息存储子服务的实现1.1 功能设计1.2 模块划分1.3 模块功能示意图1.4 数据管理1.4.1 数据库消息管理1.4.2 ES文本消息管理 1.5 接口的实现1.5.1 消息存储子服务所用到的protobuf接口实现1.5.2 最近N条消息获取接口实现1.5.3 指定时间段消息搜索接口实现1.5.4 关键字消…

数据湖的概念(包含数据中台、数据湖、数据仓库、数据集市的区别)--了解数据湖,这一篇就够了

文章目录 一、数据湖概念1、企业对数据的困扰2、什么是数据湖3、数据中台、数据湖、数据仓库、数据集市的区别 网上看了好多有关数据湖的帖子&#xff0c;还有数据中台、数据湖、数据仓库、数据集市的区别的帖子&#xff0c;发现帖子写的都很多&#xff0c;而且专业名词很多&am…