当前位置: 首页 > 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;点击确定即可一键创建分支&…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】

微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来&#xff0c;Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1)&#xff1a;从基础到实战的深度解析-CSDN博客&#xff0c;但实际面试中&#xff0c;企业更关注候选人对复杂场景的应对能力&#xff08;如多设备并发扫描、低功耗与高发现率的平衡&#xff09;和前沿技术的…...

高防服务器能够抵御哪些网络攻击呢?

高防服务器作为一种有着高度防御能力的服务器&#xff0c;可以帮助网站应对分布式拒绝服务攻击&#xff0c;有效识别和清理一些恶意的网络流量&#xff0c;为用户提供安全且稳定的网络环境&#xff0c;那么&#xff0c;高防服务器一般都可以抵御哪些网络攻击呢&#xff1f;下面…...

Redis数据倾斜问题解决

Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能

1. 开发环境准备 ​​安装DevEco Studio 3.1​​&#xff1a; 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK ​​项目配置​​&#xff1a; // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent

安全大模型训练计划&#xff1a;基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标&#xff1a;为安全大模型创建高质量、去偏、符合伦理的训练数据集&#xff0c;涵盖安全相关任务&#xff08;如有害内容检测、隐私保护、道德推理等&#xff09;。 1.1 数据收集 描…...