【JAVA EE初阶】多线程(1)

这样的代码,虽然也能打印hello thread,但是没有创建新的线程,而是直接在main方法所在的主线程中执行了run的逻辑

start方法,是调用系统api,真正在操作系统内部创建一个线程。这个新的线程会以run作为入口方法(执行run)的逻辑,run方法,不需要在代码中显式调用

此处遇见受查异常为什么不能用throws呢?

因为,如果给run方法声明加上throws,此时就意味着和父类Thread的run的方法声明不一致,就无法构成方法重写了。

package Thread;class MyThread extends Thread {@Overridepublic void run() {while(true){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}
}
public class Demo1 {
public static void main(String[] args) {System.out.println("你好!");// 创建并启动MyThread线程MyThread myThread = new MyThread();myThread.start();// myThread.run();while(true){System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}
}

输出:

但是如果换成这样的代码:

package Thread;class MyThread extends Thread {@Overridepublic void run() {while(true){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}
}
public class Demo1 {
public static void main(String[] args) {System.out.println("你好!");// 创建并启动MyThread线程MyThread myThread = new MyThread();// myThread.start();myThread.run();while(true){System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}
}

输出:

由此可见run方法并没有创建一个新的进程,从始至终只有一个主线程在运行,只有将run的方法执行完才能去打印hello main 但是由于这里的 run方法是一个死循环,所以一直没有打印hello main

通过观察我们可以发现这里的main和thread的打印,并不是严格交替的。由于两个打印,都加了sleep(1000)。当1000ms时间到的时候,这两个线程哪一个限制性,这个顺序是不确定的。

(操作系统调度线程的顺序是无需,不可预测的,随机的 )

可以通过Java jdk中的jconsole工具来观察进程

我的jdk-jconsole工具目录:C:\Program Files\Java\jdk-17\bin

折线图

这张图列出了当前进程中所有的线程。剩下的线程,都是JVM自带的,这些线程进行了一些背后的操作,比如负责垃圾回收,记录统计信息,记录一些调试信息。

如果将Thread.start()改成Thread.run()——>

我们会发现刚刚的Thread0没有了

一、Java中创建线程的方法

(1)创建子类,继承Thread,重写run,调用start

package Thread;class MyThread extends Thread {@Overridepublic void run() {while(true){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}
}
public class Demo1 {
public static void main(String[] args) {System.out.println("你好!");// 创建并启动MyThread线程MyThread myThread = new MyThread();// myThread.start();myThread.run();while(true){System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}
}
}

(2)创建子类,实现Runnable,重写run,搭配Thread对象进行start

package Thread;class MyRunnable implements Runnable {@Overridepublic void run() {while(true){System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
}public class Demo2 {public static void main(String[] args) {Runnable runnable = new MyRunnable();//runnable没有start方法,所以不能直接调用run方法,需要将runnable对象作为参数传递给Thread类的构造方法,然后调用start方法启动线程 Thread thread = new Thread(runnable);thread.start();}
}

使用Runnable描述线程要执行的任务是啥,真正去创建线程的工作,还是由Thread来负责。

interface是接口,不包含具体的方法的实现,只是提供一系列抽象方法,让子类去重写

*接口的的default一般不用,日常开发的时候,用到接口,都是希望全都提供抽象方法。除非是接口提供了一组方法,这一组方法中存在一些“公共的逻辑”,就可以在接口中搞default方法,使得这个方法表示公共逻辑,后面就可以在重写其他抽象方法的时候去调用了。

对于第一种写法(继承Thread)描述任务的时候,任务代码是写到Thread子类中的,意味着任务内容和Thread类的耦合程度更高(*写代码要高内聚/低耦合)

未来如果想把这个任务给别的“主体”去执行(执行一个任务,线程只是其中一个选项,进程也可以,协程亦可以...)

第二种写法,任务是写到Runnable中的,几乎不涉及到任何和“线程”相关的概念,任务内容和Thread概念的耦合是很小的,几乎没有

任务内容和Thread概念的耦合是很小的,几乎没有

后序就可以把这个任务交给进程、协程来执行

(*协程:近几年提出的概念,可以理解为“轻量级线程”)

(3)继承Thread,重写run,调用start 通过匿名内部类

package 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) {// TODO Auto-generated catch blocke.printStackTrace();}}}};t.start();}}

此处就是创建了没有名字的匿名内部类,这个类就是Thread的子类,子类重写了run方法,同时也创建了子类的实例,通过t引用指向。

(4)实现Runnable,重写run,搭配Thread调用start 通过匿名内部类

package Thread;public class Demo4 {public static void main(String[] args) {Runnable runnable = new Runnable() {@Overridepublic void run() {while (true) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}};Thread t = new Thread(runnable);t.start();while(true){System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}

(5)更简单的写法,基于lambda表达式创建线程

lambda表达式,本质上是“匿名方法”

package Thread;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) {// TODO Auto-generated catch blocke.printStackTrace();}}});t.start();while(true){System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}

二、后台线程与前台线程

后台线程:线程没运行完,进程可以结束(线程不能够组织进程结束)

前台线程:线程没有运行完,进程就不会结束(线程能够组织进程结束)

main线程和我们自己创建的线程都是前台线程,isDaemon = false;

剩下的线程就是后台线程,isDaemon = true;(守护线程)

通过setDaemon设置线程为后台(*必须在start之前set)

什么样子的线程是前台线程,什么样子的线程是后台线程呢?

如果一个线程做的任务很重要,这个任务必须要昨晚你,就应该设置这个线程为前台线程

如果一个线程做的任务无关紧要/周期性无期限执行(比如说JVM的垃圾回收线程),就应该设置为后台线程

package Thread;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) {// TODO Auto-generated catch blocke.printStackTrace();}}});t.setDaemon(true);t.start();//把t线程设置为守护线程,当main线程结束时,t线程也会结束for(int i = 0;i<3;i++){System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}

三、判断线程是否存活

package Thread;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) {// TODO Auto-generated catch blocke.printStackTrace();}}});System.out.println("t线程是否存活:"+t.isAlive());//false,t线程还没有启动,所以isAlive返回falset.setDaemon(true);t.start();//把t线程设置为守护线程,当main线程结束时,t线程也会结束System.out.println("t线程是否存活:"+t.isAlive());//true,t线程已经启动,所以isAlive返回truefor(int i = 0;i<3;i++){System.out.println("hello main");try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}System.out.println("t线程是否存活:"+t.isAlive());//false,t线程已经结束,所以isAlive返回false}}

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

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

相关文章

javase 学习

一、Java 三大版本 javaSE 标准版 &#xff08;桌面程序&#xff1b; 控制台开发&#xff09; javaME 嵌入式开发&#xff08;手机、小家电&#xff09;基本不用&#xff0c;已经淘汰了 javaEE E业级发开&#xff08;web端、 服务器开发&#xff09; 二、Jdk ,jre jvm 三…

【Linux】Linux 操作系统 - 05 , 软件包管理器和 vim 编辑器的使用 !

文章目录 前言一、软件包管理器1 . 软件安装2 . 包管理器3 . Linux 生态 二、软件安装 、卸载三、vim 的使用1 . 什么是 vim ?2 . vim 多模式3 . 命令模式 - 命令4 . 底行模式 - 命令5. 插入模式6 . 替换模式7 . V-BLOCK 模式8 . 技巧补充 总结 前言 本篇笔者将会对软件包管理…

python基础知识点(1)

python语句 一行写一条语句 一行内写多行语句&#xff0c;使用分号分隔建议每行写一句&#xff0c;且结束时不写分号写在[ ]、{ }内的跨行语句&#xff0c;被视为一行语句\ 是续行符,实现分行书写功能 反斜杠表示下一行和本行是同一行 代码块与缩进 代码块复合语句&#xf…

C#/.NET/.NET Core技术前沿周刊 | 第 35 期(2025年4.14-4.20)

前言 C#/.NET/.NET Core技术前沿周刊&#xff0c;你的每周技术指南针&#xff01;记录、追踪C#/.NET/.NET Core领域、生态的每周最新、最实用、最有价值的技术文章、社区动态、优质项目和学习资源等。让你时刻站在技术前沿&#xff0c;助力技术成长与视野拓宽。 欢迎投稿、推荐…

HTML表单与数据验证设计

HTML 表单与数据验证设计&#xff1a;构建可靠的用户数据采集系统 引言 互联网的核心是数据交互&#xff0c;而HTML表单是这一交互的主要入口。作为前端工程师&#xff0c;设计高质量的表单不仅关乎用户体验&#xff0c;更直接影响数据收集的准确性和系统安全。 在我的学习实…

基于STM32的Keil环境搭建与点灯

本人使用的STM32开发板为正点原子的STM32F103ZE&#xff0c;在此记录完整的搭建与点灯过程。 一、Keil的安装与配置 安装Keil 首先进入Keil下载官网&#xff1a;https://www.keil.com/download/product/ 点击MDK-ARM&#xff0c;并填写相关信息&#xff0c;之后开始下载最新版…

React-useRef

如果我们想在hooks里面获同步取最新的值&#xff0c;那么则可以使用useRef, 关键源码如下&#xff1a; function mountRef<T>(initialValue: T): {|current: T|} {const hook mountWorkInProgressHook();const ref {current: initialValue};hook.memoizedState ref;re…

幽灵依赖与常见依赖管理

文章目录 前言1. 演示&#xff1a;检测和修复幽灵依赖步骤1&#xff1a;安装 depcheck步骤2&#xff1a;在项目根目录运行 depcheck可能的输出步骤3&#xff1a;修复幽灵依赖 2. 依赖管理的好习惯 1. 场景设定现在有如下依赖需求&#xff1a; 2. 依赖冲突的表现3. 解决依赖冲突…

如何使用人工智能大模型,免费快速写工作总结?

如何使用人工智能大模型&#xff0c;免费快速写工作总结&#xff1f; 详细学习视频https://edu.csdn.net/learn/40406/666581

[Java实战经验]异常处理最佳实践

一些好的异常处理实践。 目录 异常设计自定义异常为异常设计错误代码&#xff08;状态码&#xff09;设计粒度全局异常处理异常日志信息保留 异常处理时机资源管理try-with-resources异常中的事务 异常设计 自定义异常 自定义异常设计&#xff0c;如业务异常定义BusinessExce…

Makefile 入门指南

Makefile 入门指南 最简单的例子 单文件编译 假设我们有一个main.cpp文件&#xff0c;最简单的Makefile如下&#xff1a; # 最简单的单文件编译 # 目标:依赖文件 main: main.cpp# 编译命令g main.cpp -o main使用步骤&#xff1a; 将上述内容保存为名为Makefile的文件&…

PyTorch数据操作基础教程:从张量创建到高级运算

本文通过示例代码全面讲解PyTorch中张量的基本操作&#xff0c;包含创建、运算、广播机制、索引切片等核心功能&#xff0c;并提供完整的代码和输出结果。 1. 张量创建与基本属性 import torch# 创建连续数值张量 x torch.arange(12, dtypetorch.float32) print("原始张…

【Redis】Redis中的常见数据类型(一)

文章目录 前言一、Redis前置知识1. 全局命令2、数据结构和内部编码3. 单线程架构 二、String 字符串1. 常见命令2. 计数命令3.其他命令4. 内部编码5. 典型使用场景 三、Hash哈希1. 命令2.内部编码3. 使用场景4. 缓存方式对比 结语 前言 Redis 提供了 5 种数据结构&#xff0c;…

Windows 中使用 `netstat` 命令查看端口占用

在 Windows 系统中&#xff0c;可以通过 netstat 命令来查看当前系统的网络连接以及端口的占用情况。以下是关于该命令的具体说明&#xff1a; #### 使用方法 1. **查看所有端口及其状态** 可以通过以下命令查看系统中的所有活动连接和监听端口&#xff1a; bash net…

23种设计模式-结构型模式之装饰器模式(Java版本)

Java 装饰器模式&#xff08;Decorator Pattern&#xff09;详解 &#x1f381; 什么是装饰器模式&#xff1f; 装饰器模式是一种结构型设计模式&#xff0c;允许向一个对象动态添加新的功能&#xff0c;而不改变其结构。 &#x1f9f1; 你可以想象成在原有功能上“包裹”一…

解决模拟器打开小红书设备异常问题

解决模拟器打开小红书设备异常问题 解决模拟器打开小红书设备异常问题和无法打开问题 解决模拟器打开小红书设备异常问题和无法打开问题 问题描述 最近有用户反馈在模拟器上无法正常登录和打开小红书APP&#xff0c;系统提示"设备异常"错误。本文将详细介绍如何通过…

论文阅读:2025 arxiv AI Alignment: A Comprehensive Survey

总目录 大模型安全相关研究&#xff1a;https://blog.csdn.net/WhiffeYF/article/details/142132328 AI Alignment: A Comprehensive Survey 人工智能对齐&#xff1a;全面调查 https://arxiv.org/pdf/2310.19852 https://alignmentsurvey.com/ https://www.doubao.com/cha…

精益数据分析(1/126):从《精益数据分析》探寻数据驱动增长之道

精益数据分析&#xff08;1/126&#xff09;&#xff1a;从《精益数据分析》探寻数据驱动增长之道 在当今数字化时代&#xff0c;数据无疑是企业发展的关键驱动力&#xff0c;对于竞争激烈的程序化广告行业更是如此。最近我在研读《精益数据分析》这本书&#xff0c;收获颇丰&…

第五节:React Hooks进阶篇-如何用useMemo/useCallback优化性能

反模式&#xff1a;滥用导致的内存开销React 19编译器自动Memoization原理 React Hooks 性能优化进阶&#xff1a;从手动到自动 Memoization &#xff08;基于 React 18 及以下版本&#xff0c;结合 React 19 新特性分析&#xff09; 一、useMemo/useCallback 的正确使用场景…

windows server C# IIS部署

1、添加IIS功能 windows server 2012、windows server 2016、windows server 2019 说明&#xff1a;自带的是.net 4.5 不需要安装.net 3.5 尽量使用 windows server 2019、2016高版本&#xff0c;低版本会出现需要打补丁的问题 2、打开IIS 3、打开iis应用池 .net 4.5 4、添…