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

How to use ffmpeg to convert video format from .webm to .mp4

The .mp4 container format doesn’t support the VP8 codec, which is commonly used in .webm files. MP4 containers typically use the H.264 codec for video and AAC for audio. You’ll need to re-encode the video using the H.264 codec and re-encode the audio us…...

Halcon 从XML中读取配置参数

1、XML示例 以下是一个XML配置文件的示例,该文件包含了AOI(自动光学检测)算法的环境参数和相机逻辑参数: <AOI><!--AOI算法参数 20241106--><Env><!--环境参数--><Param name="GPUName" value="NVIDIA GeForce RTX 405…...

hive表内外表之间切换

你想把内表和外表在元数据上达到切换的目的&#xff0c;这个操作有个前提&#xff0c;在apache版本源码上来讲是支持的&#xff01;&#xff01;&#xff01;&#xff01;但是&#xff01;&#xff01;&#xff01;&#xff01;注意哦&#xff01;默认情况下apache版本的源码中…...

电子邮件营销软件哪个好?

在数字化时代&#xff0c;电子邮件营销仍然是企业与客户沟通的核心策略之一。无论是推广新产品、发送新闻简报&#xff0c;还是进行客户关系管理&#xff0c;选择合适的电子邮件营销软件至关重要。面对市场上众多的选择&#xff0c;企业如何才能找到最适合自己的工具呢&#xf…...

OpenAI大事记;GPT到ChatGPT参数量进化

目录 OpenAI大事记 GPT到ChatGPT参数量进化 OpenAI大事记 GPT到ChatGPT参数量进化 ChatGPT是从初代 GPT逐渐演变而来的。在进化的过程中,GPT系列模型的参数数量呈指数级增长,从初代GPT的1.17亿个参数,到GPT-2的15 亿个参数,再到 GPT-3的1750 亿个参数。模型越来越大,训练…...

springboot020基于Java的免税商品优选购物商城设计与实现

&#x1f345;点赞收藏关注 → 文档最下方联系方式领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345; 一 、设计说明 1…...

代码随想录之字符串刷题总结

目录 1.反转字符串 2.反转字符串II 3.替换数字 4.翻转字符串里面的单词 5.右旋&&左旋字符串 1.反转字符串 题目描述&#xff1a; 编写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。 不要给另外的数组分配额外…...

PS-基础学习(常用快捷键1.2-1.3)

常用快捷键 钢笔操作功能Alt 选择工具使用选择工具放到锚点上&#xff0c;按下alt&#xff0c;然后放到调整曲度的上面&#xff0c;可以修改一边的曲度可以修改出不平滑的转折点选择工具放到锚点上进行拖拽可以移动锚点的位置ctrl 选择工具使用选择工具&#xff0c;按住ctrl…...

qt QListView详解

1、概述 QListView 是 Qt 框架中的一个视图类&#xff0c;用于展示模型中的数据。它基于 QAbstractItemView&#xff0c;支持多种视图模式&#xff0c;如列表视图&#xff08;List View&#xff09;、图标视图&#xff08;Icon View&#xff09;等。QListView 是模型/视图框架…...

287. 寻找重复数

目录 题目我的解法解法 题目 给定一个包含 n 1 个整数的数组 nums &#xff0c;其数字都在 [1, n] 范围内&#xff08;包括 1 和 n&#xff09;&#xff0c;可知至少存在一个重复的整数。 假设 nums 只有 一个重复的整数 &#xff0c;返回 这个重复的数 。 你设计的解决方案…...