【Java系列】详解多线程(一)

news2024/11/27 0:17:41

个人主页:兜里有颗棉花糖
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创
收录于专栏【Java系列专栏】
本专栏旨在分享学习Java的一点学习心得,欢迎大家在评论区交流讨论💌

目录

  • 一、背景引入
  • 二、线程的引入
    • 小总结
  • 三、进程和线程的关系(面试题)
  • 四、第一个多线程程序
    • 每个线程都能够被独立的进行调用执行

一、背景引入

在引入多线程之前,我们先来看一下进程是为了干什么的,一句话总结就是:满足"并发编程"这样的需求。

早些时候,CPU都是单核心的CPU,但是随着技术工艺变得越来越强,单核心CPU的性能的确是越来越强的,但是单核心CPU技术工艺研发到一定程度后就会遇到瓶颈,从而导致单核心CPU的研发变得非常缓慢。于是就就逐渐向多核心CPU的方向进行发展研究。

好了,既然CPU逐渐变成了多核心,那么应用程序也应该做出相应的调整来把多核心的CPU给利用起来(意思就是仅仅由硬件上的支撑是不够的,我们还要由软件上的配合),以免造成一核有难,多核围观的场景

另外,早期的系统一般是单任务系统,即在同一时刻只能使用一个程序,而无法在同一时刻内使用多个程序,完成多个任务。而要完成多个任务的话,进程就显得特别关键。

二、线程的引入

多进程能够很好的实现并发编程的效果,但还由一个缺点就是进程太重,即消耗资源更多,速度更慢。如果进程的创建和销毁比较少的话那还勉强可以,但是如果进程的创建和销毁非常频繁的话,此时就会产生非常多的开销。开销大主要体现在需要给进程分配资源,而给进程分配资源的这一过程也是需要很多时间成本的。

于是就有人提出这样一个解决方案:即在创建进程的过程中,只分配简单的PCB(进程控制块),而不去分配后续需要的内存硬盘资源,即把分配资源的时间省去,这样既可以完成多并发编程的任务又能够提升进程创建销毁的速度。

这样一种只分配简单的PCB而不去分配后续需要的内存硬盘资源的这样一种方式称为轻量级进程,也叫做线程(thread)。所以简单来说线程就是只分配PCB,但不分配后续需要的内存硬盘资源

但是线程总归是要完成任务的,如果不去分配后续需要的内存硬盘资源显然是不可以的,于是就有人想出来这样的一个办法:在创建进程的时候,先把后续需要的内存资源分配好;后续创建线程的时候让线程在进程内部直接复用先前进程分配创建好的内存资源(线程和进程直接的关系:进程包含线程)

另外,一个进程至少要包含一个线程,最初进程创建出来的时候,我们可以将这个进程视为一个只包含一个线程的进程(注意此时创建进程的过程是需要分配资源的,同时第一个线程的创建开销是可能是比较大的),我们后续再次创建线程的时候就不需要分配资源了,也就省去了分配资源的过程,因为资源已经是有了的。

小总结

好了,我们已经知道多进程已经可以完成并发编程的任务了,但是由于进程本身有些重,重就体现在进程的创建和销毁所需要的内存硬盘资源开销有些大。

所以就引入了线程(也称为轻量级进程)来更高的解决上述的问题。进程中的多个线程公共复用了进程中的各种内存硬盘等资源,同时这些进程可以各自独立的在CPU中进行调度。所以这样的话线程既可以完成并发编程的效果,又可以以轻量级的方式来完成各种任务。

另外,这里补充一个点,进程和线程是两个不同的概念,在Linux中复用了PCB这个结构体来描述线程,而在Windows中,描述进程和线程是不同的结构体。所以在Windows中,为了更好的描述进程,有时一个PCB对应一个线程,多个PCB对应一个进程(即可能是多一个PCB来描述一个进程,一个PCB来描述一个线程)。

CPU的内存指针和文件描述符表文件描述符表这两个字段内容在同一个进程是一样的,但是上下文信息、状态、优先级、记账信息......等支持调度的属性,这些PCB就不一样了(意思就是每个线程独立去CPU上进行调度,每个线程的上下文、状态、优先级、记账信息等支持调度的属性都有自已独立的一份)。

线程的引入的确能够提高任务执行的速度,同时随着线程数量的增多任务执行完成的速度当然也会增快,但是线程的数量不能是无限制提高的(CPU的核心数的数量是有限的。),同时线程数量也不是越多越好,因为线程调度的开销有时会拖慢整个程序的效率。

两句话总结:

1.进程是操作系统中进行资源分配的基本单位。
2.线程是操作系统中进程调度执行的基本单位。

三、进程和线程的关系(面试题)

1.进程包含线程,都是实现并发编程的一种方式,但是由于多进程的创建和销毁的效率并没有那么高,且线程比进程更加轻量。所以就引入了多线程的概念。

2.进程是系统分派资源的基本单位;线程是是系统调度的基本单位,创建进程的时候已经把分配资源(这里的资源主要指的是虚拟地址空间和符表)的工作做了,后续创建进程的时候直接复用之前的资源即可。

3.每个进程都有自己独立的地址空间,彼此直接不会相会影响到,从而保证了每个进程的独立性,保证了数据稳定性。
需要注意的是,如果多个组线程程共用了一份地址空间的话,一旦某个线程抛出异常的话,就可能会导致一整个进程结束(所以多个线程之间是很容易相互影响的。 )。

四、第一个多线程程序

线程本身是操作系统的概念,而操作系统又提供了API来供我们使用线程,来完成多线程编程(在Linux中称为pthread库,通过这个库我们就可以完成多线程编程)。

而在Java中,把操作系统中的API进程分装,即提供了Thread类来帮助哦我们来完成多线程编程。

Java标准库中的Thread类来自于java.lang这个包,而Java.lang这个包不需要我们手动的import

好了,光说不练是不行的,接下来我们就来看一下我们的第一个线程程序,请看下图:

package thread;

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Hello,world!");
    }
}

public class Demo01 {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }
}

现在我们来解释上述的线程程序,上述代码的MyThread类中的run()方法是重写的Thread父类中的run()方法,这个run()方法相当于线程程序的入口,线程程序跑起来之后具体要干什么事情,都是通过这个run()方法来进行描述的。

myThread.start()这个操作就是在创建一个线程(即在底层调用操作系统提供的创建线程的API,同时在操作系统内核中创建出其对应的PCB结构,并将其加入到链表中)。此时这个新创建出来的线程就会参与到cpu的调度中,接下来线程要执行的任务,就是run()方法中的内容。

接下来我如果注释掉myThread.start();,即直接执行MyThread类中的run()方法,请看下图:
在这里插入图片描述
我们来解释上述程序,我们前面已经知道,start方法会去调用操作系统提供的创建线程的API,同时在操作系统内核中创建出其所对应的PCB,此时这个新创建出来的线程就会参与cpu调度
而我们直接运行run方法的话,此时并没有调用操作系统提供的创建线程的API,也就没有创建出新的进程。
当整个程序开始运行的时候,此时会先创建出来一个java进程,这个java进程中至少包含一个进程,称为主进程,这个主进程是负责执行main()方法的线程。每一个线程都能够被独立的进行调用和执行。

每个线程都能够被独立的进行调用执行

每个线程都能够被独立的进行调用执行,现在我们来写一个程序来体现这句话。请看:

在这里插入图片描述
运行结果如下(由于程序是一个死循环,所以这里只是截取了部分的程序运行结果),如下图:
在这里插入图片描述
我们可以看到程序一会执行Hello,Linux!一会执行Hello,world!
解释:start()是新线程执行的入口,而主线程和新线程是并发执行的一个关系。先新创建出来的线程创建出来之后会执行run()方法中的代码,而main()方法则是继续往后执行。所以最终呈现出来的程序运行效果就是一会执行Hello,Linux!,一会执行Hello,world!

现在我们在来看一段程序,请看下图:
在这里插入图片描述
我们可以看到上述代码把myThread.start()注释掉了,相当于我们并没有创建线程,那么此时的话只有整个程序只有主线程这一个线程了。
分析到这里我们已经知道整个程序只有线程(也就是主线程),那么执行的run()方法就是在主线程中去执行的,因为整个程序就只有一个线程(即主线程)。

总结一下:

  • 每个线程都是一个独立的执行流。
  • 每个线程都可以执行一段代码。
  • 每个线程之间是并发的关系。

好了,本文到这里就结束了,就到这里吧,再见啦友友们!!!在这里插入图片描述

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

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

相关文章

解决git提交完代码后切换到自己分支pull拉错远程分支

🥹MD 我是猪!拉代码的时候净想着干饭了,一下给拉错了😤 🌸方法一:打印日志,然后回退版本 🌸方法二: 舍弃本地修改 git checkout .直接把冲突的文件提交到自己的分支&a…

Linux内核上游提交完整流程及示例

参考博客文章: 向linux内核提交代码 - 知乎 一、下载Linux内核源码 通过git下载Linux内核源码,具体命令如下: git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 实际命令及结果如下: penghaoDin…

【华为网络-配置-025】- 同 VLAN 下不同网段通信(启用 Sub 地址)

要求: 1、各接口配置 VLAN 后配置 Sub 地址使 PC1 与 PC3 通信。 一、sub 地址配置 [LSW1]vlan 10 [LSW1]port-group group-member GigabitEthernet 0/0/1 to GigabitEthernet 0/0/2 [LSW1-port-group]port link-type access [LSW1-port-group]port default vla…

掌握接口测试全流程,让你成为专业接口测试大师!

扫盲内容 1.为什么要做接口测试? 2.怎样做接口测试? 3.接口测测试点是什么? 4.接口测试都要掌握哪些知识? 5.其他相关知识? 一.为什么要做接口测试? ①.越底层发现bug,它的修复成本是越低…

Adobe XD学习攻略:成为设计大师的捷径!

Adobexd是什么软件?Adobexd是一站式UI/UX设计平台,结合设计和建立原型功能。用户可以使用Adobexd进行网页设计、移动应用程序设计和原型图绘制,也可以使用Adobexd软件更高效、更准确地完成静态编译和框架图到交互原型的转换。 然而&#xff…

ubuntu22.04 显卡驱动最简单的安装方法

1.拉取可选择安装的显卡驱动版本 sudo apt-get purge nvidia* #apt 的 update 和 upgrade 的区别 #apt update 命令只会获得系统上所有包的最新信息,并不会下载或者安装任何一个包。 #apt upgrade 命令来把这些包下载和升级到最新版本。 2.sudo apt update 3.安装…

如何批量给文件名加文字?

如何批量给文件名加文字?在我们的日常生活和工作中,经常会面临大量文件需要分类整理,并且可能需要在文件名后面添加一串文字作为备注。如果只是少量文件,我们可以手动逐个添加备注,但如果涉及上千甚至上万个文件&#…

vue项目下npm或yarn下安装echarts多个版本

最近在大屏展示的时候,用到了百度的echarts图表库,看完效果图后,又浏览了一下echarts官网案例,大同小异。但是搬砖过程中发现实际效果和demo相差甚远,一番折腾发现,项目中安装的是echarts4.x版本&#xff0…

重估拼多多,TEMU带给拼多多的不止是市值增量

监制 | 何玺 排版 | 叶媛 谁也没有想到,中国电商的格局改变居然如此之快。 12月29日,拼多多市值超越多年雄踞国内电商头把交椅的阿里巴巴,成为美股市值最大中概股。从此时开始,中国电商开始“拼”时代。 拼多多凭什么能超越阿里…

FFmpeg之将视频转为16:9(横屏)或9:16(竖屏)(三十六)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只…

3、Linux_系统用户管理

1.Linux 用户管理 1.1概述 Linux系统是一个多用户多任务的操作系统,任何一个要使用系统资源的用户,都必须首先向系统管理员申请一个账号,然后以这个账号的身份进入系统。root用户是系统默认创建的管理员账号。 1.2添加用户 语法 useradd […

Kubernetes入门笔记——(2)k8s设计文档

​k8s最初源自谷歌的Brog项目,架构与其类似,主要包括etcd、api server、controller manager、scheduler、kubelet和kube-proxy等组件 etcd:分布式存储,保存k8s集群的状态 api server:资源操作的唯一入口,…

如何用docker在自己服务器上部署springboot项目

一、将springboot项目打包 1、maven clean项目 2、maven package项目 打包成功之后生成jar文件(在target目录下) 3、为Java创建Dockerfile 引入jdk8的Docker镜像 FROM openjdk:8 为了使运行其余命令时更容易,让我们设置映像的工作目录。这将…

LLM之RAG实战(一):使用Mistral-7b, LangChain, ChromaDB搭建自己的WEB聊天界面

一、RAG介绍 如何使用没有被LLM训练过的数据来提高LLM性能?检索增强生成(RAG)是未来的发展方向,下面将解释一下它的含义和实际工作原理。 ​ 假设您有自己的数据集,例如来自公司的文本文档。如何让ChatGPT和其他…

响应式编程又变天了?看JDK21虚拟线程如何颠覆!

本文解释为啥会有响应式编程,为什么它在开发者中不太受欢迎,以及引入 Java 虚拟线程后它可能最终会消失。 命令式风格编程一直深受开发者喜爱,如 if-then-else、while 循环、函数和代码块等结构使代码易理解、调试,异常易追踪。然…

基于gitlab的webhook集成jenkins,并在gitlab流水线中展示jenkins的job状态信息

文章目录 1. 环境信息2. gitlab 部署3. jenkins部署4. gitlab集成jenkins4.1 jenkins的凭据上保存gitlab的账号信息4.2 jenkins中配置gitlab的连接信息4.3 编写jenkins上pipeline文件4.4 jenkins上创建pipeline项目4.5 gitlab上配置webhooks事件4.6 测试 1. 环境信息 gitlab服…

Python-Opencv图像处理的小坑

1.背景 最近在做一点图像处理的事情,在做处理时的cv2遇到一些小坑,希望大家遇到的相关的问题可以注意!! 2. cv2.imwrite保存图像 cv2.imwrite(filename, img, [params]) filename:需要写入的文件名,包括路…

前端打包环境配置步骤

获取node安装包并解压 获取node安装包 wget https://npmmirror.com/mirrors/node/v16.14.0/node-v16.14.0-linux-x64.tar.xz 解压 tar -xvf node-v16.14.0-linux-x64.tar.xz 创建软链接 sudo ln -s 此文件夹的绝对路径/bin/node /usr/local/bin/node,具体执行如下…

Unity传送门特效: The Beautiful Portal/Level up/Teleport/Warp VFX

7种不同风格的传送门特效! 每个传送门都有一个轻型和重型版本。 每个版本都有一个"无循环”和一个"无限”预制件:D 总共有28个预制件 -VFX完全使用Unity的粒子系统和基本的Unity着色器。 使用标准渲染管道中制作了这个资产。所以VFX的功能就像视频宣传片一样。 同时,…

Windows系统上如何搭建Linux操作系统

一、准备工作 1,VMware安装包 2,Centos IOS镜像 3,finalshell安装包 阿里云盘下载地址: https://www.alipan.com/s/uSQsWn15E3W 二,VMware安装 1,新建虚拟机 2,选择下一步 3,…