JavaEE-多线程上

文章目录

  • 线程概述
    • 进程/线程
    • 多线程的作用
    • JVM关于线程资源的规范
    • 关于Java程序的运行原理
  • 并发与并行
    • 并发(concurrency)
    • 并行(parallellism)
    • 并发编程与并行编程
  • 线程的调度策略
    • 分时调度模型
    • 抢占式调度模型
  • 创建线程
    • 线程类分析入门
    • 实现线程的第一种方式
    • 实现线程的第二种方式
  • 线程的生命周期
    • 线程的生命周期概述
    • 线程生命周期间的关系(含UML图)

线程概述

进程/线程

  • 进程是指操作系统中的一段程序, 它是一个正在执行的程序实例, 具有独立的内存空间和系统资源, 所以进程之间资源不共享, 如文件, 网络端口等, 在计算机运行时, 一般是先创建进程, 后创建线程, 一个进程通常可以包含多个线程
  • 线程是指的是进程中的一个执行单元, 是进程的一部分, 负责在进程中执行代码, 每一个线程都有自己的栈和程序计数器, 并且可以共享进程的资源, 多个线程可以在同一个时刻执行不同的操作, 从而提高程序的执行效率, 线程与线程之间的资源并不是完全共享的, 下面会详细介绍…

大白话总结:

  • 一个应用程序就是一个进程
  • 一个进程里面有多个线程执行任务

多线程的作用

最重要的就是提高处理问题的效率, 能够使CPU在处理一个任务时同时处理多个线程, 这样可以充分利用CPU的资源, 提高CPU的资源利用率

JVM关于线程资源的规范

我们用下面的一张图来说明
在这里插入图片描述
在同一个进程的多个线程之间, Heap(堆), Method Area(方法区)资源是共享的, Java Virtual Machine Stack(Java虚拟机栈), Native Method Stack(本地方法栈), The pc Register(程序计数器)这些都是不能够共享的

所以就存在线程安全的问题
比如对于变量来说, 局部变量存在Java虚拟机栈, 所以不存在线程安全问题, 但是实例变量, 静态变量都存在于堆中, 就会存在线程安全的问题

关于Java程序的运行原理

  • 当JVM启动的时候, JVM会自动开启一个主线程(main-thread), 然后去调用main方法
    所以main方法都是在主线程中执行的
  • 除了主线程之外, 还会启动一个垃圾回收线程(GC), 因此启动JVM, 至少启动了两个线程
  • 除上述线程之外, 程序员可以手动创建其他的线程并启动

并发与并行

早期的人们使用的单核的CPU, 那是不是不能同时运行多个程序呢? 答案是否定的, 下面的关于并发与并行就可以解释这个问题

并发(concurrency)

  • 在使用单核心CPU时候, 微观层面一个时间点只能执行一个指令, 但多个指令被快速的轮换执行,使得在宏观上具有多个指令同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干端,使多个指令快速交替的执行

下面的这张图就可以很好的说明这个问题
在这里插入图片描述
横轴对应的是时间, 纵轴对应的是ABC三个程序, 围观层面, 某一时间点只能执行一个程序, 但是通过CPU高速的在ABC三个程序中进行调度切换, 让我们宏观上看起来是三个程序同时执行的, 这种情况就是并发

并行(parallellism)

  • 这种情况针对的就是多核心CPU的情况, 此时在一个时间点, 微观层面上也可以通过多个核心同时多个程序来达到真正意义上的"同时执行"
    在这里插入图片描述

并发编程与并行编程

  • 多核心CPU资源紧缺(或者是单核心CPU)的前提下, 如果开启了多个线程, 但是只有一个CPU核心可以提供资源, 那么这些线程就会抢夺CPU的时间片, 竞争执行机会, 这就是通过 并发 的方式实现多线程
  • 多核心CPU资源比较充足的情况下, 此时有多个CPU核心可以来提供资源, 此时一个进程中的线程就会被分配到多个CPU核心上同时执行, 这就是通过 并行 的方式实现多线程
  • 不管并发还是并行,都提高了程序对CPU资源的利用率,最大限度地利用CPU资源,而我们使用多线程的目的就是为了提高CPU资源的利用率

那么Java实现多线程的方式是并发还是并行呢?

  • 至于Java多线程实现的是并发还是并行?上面所说,所写多线程可能被分配到一个CPU内核中执行,也可能被分配到不同CPU执行,分配过程是操作系统所为,不可人为控制。所以,如果有人问我我所写的多线程是并发还是并行的?我会说,都有可能

线程的调度策略

存在线程调度的原因是因为, 当多个线程被分配到同一个CPU核心执行的时候, 此时这些程序就会抢夺CPU的时间片从而或者执行权, 所以就存在执行的先后问题

分时调度模型

  • 所有线程轮流使用CPU的执行权, 并且平均每个线程的占用时间, 也就是绝对平均

抢占式调度模型

  • 让优先级高的线程以较大的概率优先获得CPU的执行权, 如果线程的优先级相同, 那么就会随机选择一个线程获得CPU的执行权, 而Java采用的就是抢占式调度模型

创建线程

线程类分析入门

这个是JDK17帮助文档的链接
JDK17帮助文档
通过API文档, 我们可以查询到我们需要的一些信息内容, 从而完成编程(这里不推荐查看中文的帮助文档)


构造方法
在这里插入图片描述
构造方法可以通过传入一个字符串然后指定该线程的名称


static Thread currentThread()
在这里插入图片描述

这是一个静态方法, 通过这个方法可以获取到当前线程的Thread信息, 其实有点类似于之前学的this


void setName(String name)

在这里插入图片描述
这是一个实例方法, 通过一个线程的引用调用之后可以设置当前线程的名称(其实每一个线程都有一个默认的名称)


String getName()

在这里插入图片描述
这是一个实例方法, 作用就是调用之后可以获得当前线程的名称


实现线程的第一种方式

执行逻辑如下

  • 创建一个类继承Thread(java.lang)包下的, 默认导入)类
  • 重写Thread中的run()方法(这个run()相当于每个线程的main函数)
  • 创建这个类的对象, 然后调用start()方法, 开启线程

我们给一个代码的案例测试一下

package thread_demo.thread_demo01;public class Thread01 {public static void main(String[] args) {// 当执行main函数的时候, 系统自动的就开启了两个线程Thread t = new MyThread();t.start();// 主线程的内容for(int i = 0; i < 100; i++){System.out.println(Thread.currentThread().getName() + "---->" + i);}}
}/*** 创建线程的第一种方式是创建一个类继承Thread(所以这个类其实也就是一个线程)* 1. 创建一个类继承Thread这个线程类(所以此时这就是一个线程)* 2. 重写run方法(这就相当于每一个线程运行的入口)* 3. new一个线程对象然后调用start()方法启动一个线程*/
class MyThread extends Thread {@Overridepublic void run() {for(int i = 0; i < 100; i++){System.out.println(Thread.currentThread().getName() + "---->" + i);}}
}

执行结果如下
在这里插入图片描述
两个线程会无规则的交替执行
初学者好多不理解这个代码的执行逻辑, 首先我们在main中创建了一个线程对象, start()的作用就是开启一个线程, 然后就弹栈了, 此时JVM中就存在了两个线程同时执行(不含GC), 所有就会出现交替执行的情况

实现线程的第二种方式

执行逻辑如下

  • 创建一个类实现Runnable接口
  • 重写其中的run()方法
  • 创建这个类的对象(匿名的也可以), start()开启一个线程

给一个代码案例测试一下

package thread_demo.thread_demo02;public class Thread02 {public static void main(String[] args) {// 直接new一个对象调用创建一个t线程Runnable r = new MyThread();Thread t = new Thread(r, "test1");// 开启t线程t.start();// 利用匿名内部类的方式创建一个线程对象Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + "--->" + i);}}}, "test2");// 开启t1线程t1.start();// 直接就不接收变量直接开启一个线程new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + "--->" + i);}}}, "test3").start();// 最后在操作一下主线程Thread mainThread = Thread.currentThread();mainThread.setName("main");for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName() + "--->" + i);}}
}/*** 创建线程的第二种方式是定义一个类去实现Runnable接口, 我们推荐使用这种方式开启多线程* 1. 定义一个类实现Runnable接口* 2. 重写run方法* 3. Thread t = new Thread(传入这个类的对象)* 4. t.start() 去开启一个线程*/
class MyThread implements Runnable{@Overridepublic void run() {for(int i = 0; i < 100; i++){System.out.println(Thread.currentThread().getName() + "--->" + i);}}
}

执行结果也是多个线程交替执行, 原理和第一种是一致的
在这里插入图片描述

线程的生命周期

线程的生命周期概述

线程的生命周期主要就是指的一个线程在不同时期的状态情况, 大致可以分

  • 6种(JDK层面)
  • 7种(我们平时常说的)

首先我们从JDK层面分析一下为什么是6种
我们查看一下帮助文档找到Thread.State这个枚举类型
在这里插入图片描述
在这里插入图片描述

  • NEW : 新建状态, 也就是执行start()之前的状态
  • RUNNABLE : 可运行状态, 这个状态分为两个子状态
    就绪状态 和 运行状态
  • WAITING : 等待状态, 不限时间(比如等待用户输入)
  • TIMED_WAITING : 超时等待状态, 限制时间(比如Thread.sleep(毫秒)
  • BLOCKED : 阻塞状态, 比如遇到了一些关于锁的操作
  • TERMINATED : 时亡状态, run()方法结束

线程生命周期间的关系(含UML图)

下面我们尝试绘制一个UML来描述一下各个状态之间的关系
在这里插入图片描述
图上说明的十分的详细了…, 我们再阐述一下

  • 首先线程处于一个NEW的新建状态, 然后通过start()创建一个线程, 此时线程处于RUNNABLE(就绪状态)
  • 位于RUNNABLE(就绪状态)的线程拥有抢夺CPU时间片的能力, 当抢夺到了CPU时间片之后, 线程的run()方法就开始执行, 此时线程处于RUNNABLE(运行状态), 当抢夺到的CPU时间片用完了之后, 线程会再次进入到RUNNABLE(就绪状态), 下次执行时会接着上一次的执行, 这个过程中靠的是CPU的调度机制
  • 当程序在RUNNABLE(运行状态)遇到Thread.sleep(毫秒)类似的间隔的时候, 会进入到TIMED_WAITING超时等待状态, 此时会返还先前抢到的CPU时间片, 所有的等待休眠期度过之后, 会进入到RUNNABLE(就绪状态)等待下一次抢夺CPU时间片
  • 当程序在RUNNABLE(运行状态)遇到例如接收到需要等待用户输入的指令的时候, 就会进入到WAITING等待状态, 这个等待状态是没有时间的限制的(比如接收到输入为止)
  • run()方法彻底执行结束之后, 就会触发到TERMINATED状态…

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

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

相关文章

SQL 常用语句

目录 我的测试环境 学习文档 进入数据库 基础通关测验 语句-- 查 展示数据库&#xff1b; 进入某个数据库&#xff1b; 展示表&#xff1a; 展示某个表 desc 查询整个表&#xff1a; 查询特定列&#xff1a; 范围查询 等于特定值 不等于 介于 特定字符查询 Li…

[MySQL]DQL语句(一)

查询语句是数据库操作中最为重要的一系列语法。查询关键字有 select、where、group、having、order by、imit。其中imit是MySQL的方言&#xff0c;只在MySQL适用。 数据库查询又分单表查询和多表查询&#xff0c;这里讲一下单表查询。 基础查询 # 查询指定列 SELECT * FROM …

【Unity】鼠标点击获取世界坐标位置:物体移动至鼠标点击的位置

需求说明 鼠标点击3D场景时&#xff0c;可以获取其所在的世界坐标&#xff1b; 鼠标点击3D物体时&#xff0c;可以获取该物体&#xff1b; 鼠标点击3D物体时&#xff0c;可以让玩家移动至该物体&#xff1b; 成果展示 Scene部分 关于仓库栏的设置&#xff0c;物体如何进入…

使用nvm切换node版本失败

​ 使用nvm切换node版本失败&#xff08;原node版本v20.14.0&#xff0c;我使用nvm use 16.9.1切换node版本后&#xff0c;显示Now using node v16.9.1可当我使用命令node -v查看当前node版本时还是v20.14.0&#xff0c;意味着版本切换失败&#xff09;&#xff1a; 这个原因大…

Hive数据库操作语法

数据类型 内部表和外部表 内部表 &#xff08;CREATE TABLE table_name ......&#xff09;未被external关键字修饰的即是内部表&#xff0c; 即普通表。 内部表又称管理表,内部表数据存储的位置由hive.metastore.warehouse.dir参数决定&#xff08;默认&#xff1a;/user/h…

【Python TensorFlow】入门到精通

TensorFlow 是一个开源的机器学习框架&#xff0c;由 Google 开发&#xff0c;广泛应用于机器学习和深度学习领域。本篇将详细介绍 TensorFlow 的基础知识&#xff0c;并通过一系列示例来帮助读者从入门到精通 TensorFlow 的使用。 1. TensorFlow 简介 1.1 什么是 TensorFlow…

设计模式08-行为型模式1(命令模式/迭代器模式/观察者模式/Java)

五、行为型模式 **行为模式的定义&#xff1a;**行为型模式是对不同的对象之间划分职责和算法的抽象化。行为型模式定义了系统中对象之间的交互与通信&#xff0c;研究系统在运行时对象之间的相互通信与协作&#xff0c;进一步明确对象的职责&#xff0c;包括对系统中较为复杂的…

服务器作业2

关闭防火墙 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 配置文件 创建用户nfs-upload [rootlocalhost ~]# useradd -u 210 nfs-upload [rootlocalhost ~]# groupmod -g 210 nfs-upload 创建tom用户 [rootlocalhost ~]# useradd tom 查看to…

【数据结构】堆:建堆/向下调整/上向调整/堆排序/TOK问题

文章目录 前言堆的定义1.大小堆2.完全二叉树 堆的实现堆的数据结构初始化销毁取堆顶元素判断堆是否为空父结点和子结点下标关系&#xff08;重要&#xff09; 向下调整法-O(n)小堆版大堆版 向上调整法-nlog(n)堆的插入和删除插入(调用向上调整)删除(调用向下调整) 构建最大堆向…

java学习1

一、运算符 1.算术运算符 在代码中&#xff0c;如果有小数参与计算&#xff0c;结果有可能不精确 1-1.隐式转换和强制转换 数字进行运算时&#xff0c;数据类型不一样不能运算&#xff0c;需要转成一样的&#xff0c;才能运算 &#xff08;1&#xff09;隐式转换&#xff1a…

20.体育馆使用预约系统(基于springboot和vue的Java项目)

目录 1.系统的受众说明 2.开发环境与技术 2.1 Java语言 2.2 MYSQL数据库 2.3 IDEA开发工具 2.4 Spring Boot框架 3.需求分析 3.1 可行性分析 3.1.1 技术可行性 3.1.2 经济可行性 3.1.3 操作可行性 3.2 系统流程分析 3.3 系统性能需求 3.4 系统功能需求 4.系…

Halcon3D image_points_to_world_plane详解

分三个部分来聊聊这个算子 一,算子的参数介绍 二,算法的计算过程 三,举例实现 第一部分,算子的介绍 image_points_to_world_plane( : : CameraParam, WorldPose, Rows, Cols, Scale : X, Y) 参数介绍: CameraParam,:相机内参 WorldPose 世界坐标系,也叫物体坐标系(成…

【启程Golang之旅】并发编程构建简易聊天系统

欢迎来到Golang的世界&#xff01;在当今快节奏的软件开发领域&#xff0c;选择一种高效、简洁的编程语言至关重要。而在这方面&#xff0c;Golang&#xff08;又称Go&#xff09;无疑是一个备受瞩目的选择。在本文中&#xff0c;带领您探索Golang的世界&#xff0c;一步步地了…

无人机场景 - 目标检测数据集 - 夜间车辆检测数据集下载「包含VOC、COCO、YOLO三种格式」

数据集介绍&#xff1a;无人机场景夜间车辆检测数据集&#xff0c;真实场景高质量图片数据&#xff0c;涉及场景丰富&#xff0c;比如夜间无人机场景城市道路行驶车辆图片、夜间无人机场景城市道边停车车辆图片、夜间无人机场景停车场车辆图片、夜间无人机场景小区车辆图片、夜…

HTML学习笔记十

系列笔记目录 第一章 HTML的概述 第二章 URL简介 第三章 网页元素的属性 第四章 html字符编码 第五章 网页的语义结构 第六章 文本标签 第七章 列表标签 第八章 图像标签 第九章 链接标签 第十章 多媒体标签 多媒体标签 系列笔记目录前言一、简介二、常用标签2.1<video>2…

Thumb 汇编指令集,Thumb 指令编码方式,编译 Thumb 汇编代码

版权归作者所有&#xff0c;如有转发&#xff0c;请注明文章出处&#xff1a;https://cyrus-studio.github.io/blog/ Thumb指令集 ARM 指令集&#xff1a;最早在 1985 年随第一代 ARM 处理器问世。ARM 指令集一开始是 32 位固定长度的指令&#xff0c;用于各种计算任务。 Thu…

【Clikhouse 探秘】ClickHouse 物化视图:加速大数据分析的新利器

&#x1f449;博主介绍&#xff1a; 博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家&#xff0c;WEB架构师&#xff0c;阿里云专家博主&#xff0c;华为云云享专家&#xff0c;51CTO 专家博主 ⛪️ 个人社区&#x…

【HarmonyOS NEXT】在 HarmonyOS NEXT 中实现优雅的加载动画

【HarmonyOS NEXT】在 HarmonyOS NEXT 中实现优雅的加载动画 在移动应用开发中&#xff0c;加载动画是提升用户体验的重要工具。在应用程序处理数据或加载页面时&#xff0c;为用户提供视觉反馈尤为关键。在这篇博客中&#xff0c;我们将探讨如何在 HarmonyOS NEXT 中使用 Sta…

Redis高级篇之缓存一致性详细教程

文章目录 0 前言1.缓存双写一致性的理解1.1 缓存按照操作来分 2. 数据库和缓存一致性的几种更新策略2.1 可以停机的情况2.2 我们讨论4种更新策略2.3 解决方案 总结 0 前言 缓存一致性问题在工作中绝对没办法回避的问题&#xff0c;比如&#xff1a;在实际开发过程中&#xff0c…

C++_day2

目录 1. 引用 reference&#xff08;重点&#xff09; 1.1 基础使用 1.2 特性 1.3 引用参数 2. C窄化&#xff08;了解&#xff09; 3. 输入&#xff08;熟悉&#xff09; 4. string 字符串类&#xff08;掌握&#xff09; 4.1 基础使用 4.2 取出元素 4.3 字符串与数字转换 5. …