当前位置: 首页 > news >正文

【多线程入门篇】 创建线程以及线程的属性

在这里插入图片描述

大家好呀
我是浪前

今天给大家讲解的是创建线程以及线程的属性

祝愿所有点赞关注的人,身体健康,一夜暴富,升职加薪迎娶白富美!!!
点我领取迎娶白富美大礼包

🍓多线程编程:

前言:
我们为什么不用多进程?

多进程相关的API在Java标准库中没有提供
Java适合使用多线程来进行编程:

多线程在并发编程的时候,效率更高
尤其对于Java进程是要启动Java虚拟机来说
启动虚拟机这个事情开销很大, 搞多个Java进程就是搞多个Java虚拟机

于是java使用了标准库把与多线程编程有关API给封装了

比如在Java中的Thread类

一个进程中至少有一个线程
这个进程中的第一个线程就叫做主线程

main方法就是主线程的入口方法

每个线程都是一个独立的执行流,相互独立执行,互不干扰

线程的执行顺序是不确定的,是随机的

为什么这里是随机的呢?
因为操作系统中有一个调度器模块,这个模块的实现方式就是类似于一种随机调度的效果

🍓随机调度:

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

随机调度这种也叫抢占式执行:
这个特性会导致多线程安全问题

Windows等等主流的操作系统都是抢占式执行

🍓观看线程的详细信息:

使用一个程序来观看线程的详细信息
在jdk中的bin目录之下有一个jconsole.exe的程序
在这里插入图片描述

在这里插入图片描述

在使用这个jconsole.exe程序查看线程的详细信息之前要先确保两个点:

  1. 确保你的程序(线程)已经先跑起来了
  2. 有些需要使用管理员方式来运行

如图所示:
在这里插入图片描述

在这里插入图片描述

🍓sleep

线程中的while循环转得太快了, 使用sleep方法来休眠,使得循环转得慢一些,
sleep是Thread的静态方法, 属于Thread类

我们可以在线程中加入sleep 来降低循环速度

在这里插入图片描述
时间单位换算:
1s = 1000ms
1 ms = 1000us
1us = 1000ns

🍓创建线程

线程的创建有好几种方式:

  1. 继承Thread类, 重写run方法
  2. 实现Runnable接口
  3. 还是继承Thread,重写run,但是使用匿名内部类
  4. 还是实现那Runnable, 重写run, 也是使用匿名内部类
  5. 基于Lambda表达式)最推荐的方式

🍓第一种: 继承Thread类, 重写run方法:

class MyThread extends Thread{  @Override  public void run(){  while(true){  System.out.println("hello thread");  try {  Thread.sleep(1000);  } catch (InterruptedException e) {  e.printStackTrace();  }  }  }  
}  public class ThreadDemo01 {  public static void main(String[] args) throws InterruptedException {  Thread t = new MyThread();  t.start();  while (true) {  System.out.println("hello main");  Thread.sleep(1000);  }  }  
}

结果如图所示:
在这里插入图片描述

🍓第二种:实现Runnable接口

class MyRunnable implements Runnable{  @Override  public void run(){  System.out.println("hello Runnable");  try {  Thread.sleep(2000);  } catch (InterruptedException e) {  e.printStackTrace();  }  }  
}  public class Text2 {  public static void main(String[] args) throws InterruptedException {  Thread t2 = new Thread(new MyRunnable());  t2.start();  while(true){  System.out.println("hello main");  Thread.sleep(2000);  }  }  
}

结果如图所示:
在这里插入图片描述

🍓注意事项:

我们使用Runnable接口的方式和直接继承Thread类的方式有什么区别吗?
我们使用Runnable接口的方式有利于我们进行解耦合

🍓解耦合:

那什么是解耦合?
我们在创建一个线程,是需要两个关键操作的:

  1. 明确线程要执行的任务
  2. 调用系统的API创建出线程

那么此时若我们使用的是Runable接口
那么我们就可以把任务单独提取出来,提取出来之后

就可以随时把代码改成使用其他方式来执行这个任务

举个例子:
现在有一个一家三口,父亲,母亲,和儿子,现在家里面没有酱油了,此时就需要去执行“买酱油”这个任务

那么我们就可以把“买酱油”这个任务单独提取出来,之后这个任务是交给父亲执行,还是母亲执行,还是儿子执行都是没有本质区别的

而在代码中就是把这个任务单独提取成Runnable, 后续是谁来执行都可以进行轻松的调整
这个就是解耦合

🍓Runnable

Runnable可以理解为可执行的
作用:

通过这个接口就可以抽象表示出一段可以被其他实体来执行的代码~~

在代码中的run方法就是这个Runnable要表示的一段代码

class MyRunnable implements Runnable{  @Override  public void run(){  System.out.println("hello Runnable");  try {  Thread.sleep(2000);  } catch (InterruptedException e) {  e.printStackTrace();  }  }  
}

但是这个Runnable只是一段可以执行的代码,还是要搭配Thread类,才能够真正地在系统中创建出线程
就是把线程和要执行的任务进行了解耦合:
如下所示:

Thread t = new Thread(new MyRunnable());

🍓第三种 : 继承Thread类, 重写run方法,使用匿名内部类:

匿名内部类 :

在一个类中定义的类,没有名字, 也就不能够重复使用,用一次就扔了

这个匿名内部类是Thread的子类, 同时又把这个匿名内部类的实例给创建出来

而且这个匿名内部类是可以重写run方法的

public class Demo3 {  public static void main(String[] args) throws InterruptedException {  Thread t = new Thread(){  @Override  public void run(){  while(true){  System.out.println("hello thread");  try {  Thread.sleep(1000);  } catch (InterruptedException e) {  e.printStackTrace();  }  }  }  };  t.start();  while(true){  System.out.println("hello main");  Thread.sleep(1000);  }  }  
}

🍓第四种: 实现Runnable接口, 重写run, 使用匿名内部类

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

🍓第五种: 基于Lambda表达式(最推荐的方式)

Lambda表达式是更简洁的语法表示方式: (语法糖)

以下就是一个for循环的语法糖

for(int x : arr)

Lambda表达式如下:

Thread t= new Thread(() -> {
});

Lambda表达式:

Lambda表达式是一个匿名函数, (无名函数; 一次性的)
主要是用来实现回调函数的效果的

回调函数:

回调函数 :

不是程序员主动调用, 也不是现在立即调用
而是把调用的机会交给别人(操作系统, 库, 框架, 别人写的代码)
交给别人之后在合适的时机来进行调用

Lambda表达式的本质

Lambda表达式本质上就是一个函数式接口,通过函数式接口来描述一个方法,本质上还是没有脱离类

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

在这里插入图片描述

🍓线程的其他属性:

构造方法:

Thread(String name) :

name不会影响到线程的执行, 就只是给线程取不同的名字;为了方便调用和调试
而且线程间的名字是可以重复的,但是要起一个有意义的名字

例子:
创建一个线程, 命名为 " 这个是一个新线程"

public class Demo5 {  public static void main(String[] args) throws InterruptedException {  Thread t = new Thread(() -> {  while(true){  System.out.println("hello thread");  try {  Thread.sleep(1000);  } catch (InterruptedException e) {  e.printStackTrace();  }  }  },"这个是新线程");  t.start();  while(true){  System.out.println("hello main");  Thread.sleep(1000);  }  }  
}

在这里插入图片描述

getId() :

JVM自动分配的身份标识,会保证唯一性, 标识一个进程中唯一的一个线程
这个ID是java给你这个线程分配的ID, 不是系统API给你分配的, 也不是PCB中的ID

getState() :

线程的状态,显示线程是就绪状态还是阻塞状态)

getPriority() :

线程的优先级: 由于系统是随机调度的方式
在java中设置优先级效果不明显;只是对内核调度器的调度过程产生了一些影响:

isDaemon():

描述当前线程是否是守护线程:(后台线程)

t.setDaemon(true); //设置为后台线程

不写这个代码就默认是前台线程

🍓前台线程与后台线程的区别:

后台线程: 后台线程运行不会阻止进程结束

前台线程: 前台线程运行会阻止进程结束

我们创建的代码默认是前台线程,在运行过程中会阻止进程结束
只要前台进程没有执行完毕,那么进程就不会结束
即使main方法已经执行完毕了,进程也不会结束

如下图所示:

在这里插入图片描述

在这里插入图片描述

isAlive() :

表示内核中的线程(PCB)是否存在

java代码中定义的线程对象(Thread) 实例, 虽然表示一个线程
但是这个对象本身的生命周期和内核中PCB的生命周期不完全一样

Thread t = new Thread()

此时t对象有了,但是内核PCB还没有,isAlive 就是false

当执行了下面的代码之后,才创建了PCB:

t.start();

此时才有了内核PCB, 才真正的在内核中创建了这个PCB, 此时isAlive() 就是true

在这里插入图片描述

start()

Thread类使用start方法来启动一个线程

对于同一个Thread类来说, start()方法只能够调用一次

在这里插入图片描述

🍓start() 和 run() 方法的区别

有一个经典的面试题:
start() 和 run() 方法的区别是什么?

如果是调用run()方法来执行, 那么就没有创建新的线程
在代码中只有main这个主线程, 在代码中只有一个线程,
此时这个主线程就只能够停留在run方法中的循环里面, 一直打印hello thread

不会去执行mian方法中下方的while循环的代码,也就不会打印hellow main

如图:
在这里插入图片描述

那如果是调用的start() 方法来执行代码,则会创建一个新的线程

而这个新的线程就会去执行run()方法中的循环, 来打印hellow thread
而main方法中的主线程就会去继续向下执行下方的while循环中的代码了

也就会在循环中不断地打印hellow main , 此时是有两个线程同时执行的,

第一个是通过start()创建出来的新线程 在打印run()方法中代码
第二个是main主线程在执行while循环中的代码

如图:
在这里插入图片描述

🍓总结:

两者的区别如下:

start方法的内部是调用系统的API
在系统内核中创建出线程,然后再由这个线程去执行run方法

run方法是单纯描述了这个线程的要执行什么内容
这个run方法会在start方法创建好线程之后自动被调用
没有创建出新的线程, 只是一个run方法

在这里插入图片描述

相关文章:

【多线程入门篇】 创建线程以及线程的属性

大家好呀 我是浪前 今天给大家讲解的是创建线程以及线程的属性 祝愿所有点赞关注的人,身体健康,一夜暴富,升职加薪迎娶白富美!!! 点我领取迎娶白富美大礼包 🍓多线程编程: 前言: 我们为什么不用多进程?…...

三十四、Python基础语法(文件操作-上)

一、介绍 文件:可以储存在长期储存设备上的一段数据,在计算机储存的数据都是二进制的形式储存的,我们用软件打开文件不是看见0和1是因为软件会自动将二进制数据进行转换。 二、文件操作 1.打开文件 打开文件:文件是在硬盘中储…...

【大咖云集,院士出席 | ACM独立出版】第四届大数据、人工智能与风险管理国际学术会议 (ICBAR 2024,11月15-17日)--冬季主会场

第四届大数据、人工智能与风险管理国际学术会议 (ICBAR 2024)--冬季主会场 2024 4th International Conference on Big Data, Artificial Intelligence and Risk Management 官方信息 会议官网:www.icbar.net 2024 4th International Conference on Big Data, Art…...

03 Oracle进程秘籍:深度解析Oracle后台进程体系

文章目录 Oracle进程秘籍:深度解析Oracle后台进程体系一、Oracle后台进程概览1.1 DBWn(Database Writer Process)1.2 LGWR(Log Writer Process)1.3 SMON(System Monitor Process)1.4 PMON&#…...

AndroidStudio通过Bundle进行数据传递

作者:CSDN-PleaSure乐事 欢迎大家阅读我的博客 希望大家喜欢 使用环境:AndroidStudio 目录 1.新建活动 2.修改页面布局 代码: 效果: 3.新建类ResultActivity并继承AppCompatActivity 4.新建布局文件activity_result.xml 代…...

Linux篇(文件管理命令)

目录 一、Linux下文件命名规则 1. 可以使用哪些字符 2. 文件名的长度 3. 文件名的大小写 4. Linux文件扩展名 二、目录创建与删除 1. 目录创建 1.1. mkdir创建目录 1.2. mkdir -p 递归创建目录 1.3. 使用mkdir同时创建多个目录 2. 目录删除(必须是空目录&…...

大数据新视界 -- 大数据大厂之 Impala 性能优化:数据存储分区的艺术与实践(下)(2/30)

💖💖💖亲爱的朋友们,热烈欢迎你们来到 青云交的博客!能与你们在此邂逅,我满心欢喜,深感无比荣幸。在这个瞬息万变的时代,我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…...

【数据结构】B树

B树(B-Tree)是一种自平衡的多叉搜索树,广泛应用于数据库系统和文件系统中,以便高效地进行数据存储和检索。它的设计目标是减少磁盘I/O操作,使得在大量数据的情况下依然能够进行快速的查找、插入和删除操作。 B树的特点…...

Docker 容器网络模式详解

Docker 容器网络模式详解 1.1 引言 1.1.1 Docker 网络简介 Docker 是一个开源的应用容器引擎,它允许开发者将应用和依赖打包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器采用沙箱机制,彼此…...

吴恩达深度学习笔记:卷积神经网络(Foundations of Convolutional Neural Networks)4.11

目录 第四门课 卷积神经网络(Convolutional Neural Networks)第四周 特殊应用:人脸识别和神经风格转换(Special applications: Face recognition &Neural style transfer)4.11 一维到三维推广(1D and 3…...

小游戏开发,出现了降本增效的技术?

中国经济下行大周期下,要说受影响程度较小的,非游戏行业莫属了。 小游戏的快速增长主要得益于其便捷的使用方式和轻量化的特点。小游戏通常无需下载,即点即玩,适合在碎片时间内进行娱乐,这种特性吸引了大量用户。此外…...

(4)Java 编程基础概览:Java中的输入输出操作与代码注释详解

目录 1. 控制台输出操作2. 控制台输入操作代码解释:3. 代码注释3.1 单行注释3.2 多行注释3.3 文档注释3.4 注释的重要性3.5 注意事项在Java编程语言中,输入与输出(I/O)操作扮演着举足轻重的角色。它们允许程序与外界环境进行数据的交互,无论是从用户处获取信息,还是向用户…...

Git使用指南

目录 工作机制基本框架:流程图 基本命令分支操作远程仓库本地仓库关联远程仓库 参考 工作机制 基本框架: Workspace:开发者工作区,也就是你当前写代码的目录,它一般保持的是最新仓库代码。Index / Stage:暂存区,最早…...

【linux】再谈网络基础(一)

1. 再谈 "协议" 协议是一种 "约定",在读写数据时, 都是按 "字符串" 的方式来发送接收的. 但是这里我们会遇到一些问题: 如何确保从网上读取的数据是否是完整的,区分缓冲区中的由不同客户端发来的数据 2. 网…...

Unknown at rule @tailwindscss(unknownAtRules)

一、前言 整合 tailwindcss 后,发现指令提示警告 Unknown at rule tailwindscss(unknownAtRules),其实是 vscode 无法识别 tailwindscss 指令,不影响使用,但是对于我这种有编程洁癖的人来说,有点膈应。 二、解决方案…...

IDEA - 快速去除 mapper.xml 黄色警告线和背景色----简化版

1.打开设置 2.去掉黄色警告线设置 3.去掉背景色设置 4.示范图...

高级 SQL 技巧详解

文章目录 高级 SQL 技巧详解一、引言二、窗口函数1、窗口函数的使用1.1、RANK() 函数示例1.2、常用窗口函数 三、公共表表达式(CTE)2、CTE 的使用2.1、CTE 示例 四、索引优化3、索引的创建与优化3.1、创建索引3.2、索引类型与注意事项 五、事务管理4、事…...

移除元素(java)

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。 假设 nums 中不等于 val 的元素数量为 k,要通过此题,您需要执行以下操作: 更改…...

【Linux】shell脚本:检测文件是否存在,如存在则删除

通常&#xff0c;可以使用[ -f <file> ]来检查文件是否存在&#xff0c;使用rm <file>来删除文件。 以下是一个示例脚本&#xff0c;用于检测一个文件是否存在&#xff0c;并在存在时删除它&#xff1a; #!/bin/bash # 定义要检查的文件路径 file_path"/…...

Git代码托管(三)可视化工具操作(1)

常见的可视化操作工具有 一、官方网页 如码云、gitlab&#xff0c;自带了常见的git操作。 以码云为例&#xff1a; 1、创建分支&#xff1a; 进入分支目录&#xff0c;点击 新建分支 按钮&#xff0c; 在弹出框中输入新分支名称&#xff0c;点击确定即可一键创建分支&…...

如何永久免费解锁Cursor Pro全部功能:终极解决方案完全指南

如何永久免费解锁Cursor Pro全部功能&#xff1a;终极解决方案完全指南 【免费下载链接】cursor-free-vip [Support 0.45]&#xff08;Multi Language 多语言&#xff09;自动注册 Cursor Ai &#xff0c;自动重置机器ID &#xff0c; 免费升级使用Pro 功能: Youve reached you…...

音视频开发避坑:YUV420P图像处理时Stride不对齐,你的内存拷贝为啥总出错?

音视频开发避坑&#xff1a;YUV420P图像处理时Stride不对齐&#xff0c;你的内存拷贝为啥总出错&#xff1f; 在音视频开发中&#xff0c;YUV420P格式因其高效的存储方式被广泛使用&#xff0c;但许多开发者在处理这类图像时&#xff0c;常常会遇到内存拷贝错误、程序崩溃或画面…...

从游戏UI到工业HMI:聊聊Qt自定义控件(仪表盘、雷达、摇杆)的设计思路复用

从游戏UI到工业HMI&#xff1a;Qt自定义控件的跨领域设计思维 在数字界面设计领域&#xff0c;游戏UI与工业HMI看似分属两个极端——前者追求炫酷动效与沉浸体验&#xff0c;后者强调信息清晰与操作可靠。但当我们拆解那些优秀的仪表盘、雷达扫描和交互摇杆控件时&#xff0c;会…...

Cadence软件安装后找不到图标?别慌,手把手教你从开始菜单启动Capture和Allegro

Cadence软件安装后找不到图标&#xff1f;别慌&#xff0c;手把手教你从开始菜单启动Capture和Allegro 刚完成Cadence软件安装的兴奋感&#xff0c;往往会被桌面上空空如也的现状瞬间浇灭。这就像拿到一台新电脑却发现没有电源键——明明安装了专业EDA工具&#xff0c;却连入口…...

告别手写!用Playwright Codegen录制脚本,5分钟搞定百度搜索自动化

零代码神器&#xff1a;Playwright Codegen 5分钟实现百度搜索全流程自动化 每次手动测试网页功能时&#xff0c;你是否也厌倦了重复点击、输入、验证的机械操作&#xff1f;对于没有编程背景的测试人员或刚接触自动化的开发者来说&#xff0c;Playwright Codegen就像一位隐形的…...

django-tenants测试策略:单元测试、集成测试与持续集成

django-tenants测试策略&#xff1a;单元测试、集成测试与持续集成 【免费下载链接】django-tenants Django tenants using PostgreSQL Schemas 项目地址: https://gitcode.com/gh_mirrors/dj/django-tenants django-tenants是一个基于PostgreSQL模式的Django多租户解决…...

Taotoken多模型聚合在批量内容生成任务中的稳定性观察

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 Taotoken多模型聚合在批量内容生成任务中的稳定性观察 1. 任务背景与挑战 在涉及大规模、长时间运行的内容生成任务中&#xff0c…...

CentOS 8 Stream换源踩坑记:从阿里云到清华源,哪个更适合你的服务器?

CentOS 8 Stream镜像源深度评测&#xff1a;阿里云、清华源与网易163实战对比 当你在凌晨三点被服务器告警吵醒&#xff0c;发现安全补丁因下载超时无法安装时&#xff0c;一个可靠的软件源就成了救命稻草。作为国内使用最广泛的RHEL系社区发行版&#xff0c;CentOS 8 Stream的…...

Hitboxer:专业级SOCD按键重映射工具,3分钟解决游戏输入冲突

Hitboxer&#xff1a;专业级SOCD按键重映射工具&#xff0c;3分钟解决游戏输入冲突 【免费下载链接】socd Key remapper for epic gamers 项目地址: https://gitcode.com/gh_mirrors/so/socd 还在为游戏中同时按下相反方向键导致角色卡顿而烦恼吗&#xff1f;Hitboxer是…...

本地化新闻查询为何总延迟超800ms?Perplexity边缘推理优化方案,实测响应压降至127ms,附Benchmark对比表

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;本地化新闻查询为何总延迟超800ms&#xff1f;Perplexity边缘推理优化方案&#xff0c;实测响应压降至127ms&#xff0c;附Benchmark对比表 本地化新闻查询高延迟的根本症结&#xff0c;在于传统云端大模型推…...