Java并发编程实战 01 | 进程和线程

news2024/11/15 11:06:07

最早的计算机就像一个新手服务员,只有在接收到每一条指令时才会开始执行。当用户输入指令时,计算机会执行这条指令,然后等待下一条指令。如果用户在思考或者犹豫时,计算机就会乖乖地等待,效率实在是有点低,因为计算机有很多时间是闲着的。

批处理操作系统

后来,批处理操作系统出现了,它就像一个升级版的服务员,能够一次性接收一系列指令,并按顺序执行。这种系统允许用户将要执行的程序写入一盘磁带中,然后让计算机读取这些指令并执行,同时将结果写入另一盘磁带上。

虽然批处理操作系统在一定程度上提高了计算机的运行效率,但由于它仍然是串行处理的,内存中一次只能运行一个程序。后续的程序必须等到前一个程序完全执行完毕后才能开始执行。如果前一个程序因为I/O操作、网络请求等原因被阻塞,整个批处理的运行效率就会受到影响。因此,批处理操作系统的效率依然有限。

进程

随着科技的发展,人们对计算机性能的要求越来越高,现有的批处理操作系统已无法满足这些需求。批处理操作系统的瓶颈在于一次只能运行一个程序。

为了解决批处理操作系统的瓶颈,科学家们开发出了多任务操作系统,在多任务操作系统的诞生过程中,他们提出了一个新的概念——进程。

进程就是正在运行的程序。比如,当你启动一个 Java 程序时,实际上是启动了一个 Java 虚拟机进程。换句话说,一个正在运行的 Java 程序就是一个 Java 虚拟机进程。这个概念使得计算机能够同时运行多个程序,从而大大提高了计算机的效率和性能。

操作系统可以同时运行多个进程,比如 Chrome 浏览器、微信 等,它们之间互不干扰。每个进程都会保存自身的运行状态,操作系统能够高效地管理和切换这些进程。

为实现这一点,CPU 使用时间片轮转调度算法来运行进程。这个算法的基本原理是:CPU 为每个进程分配一个固定的时间段,称为时间片。进程在运行时如果分配给它的时间片用完了,该进程会被暂停,然后操作系统将 CPU 分配给另一个进程,这个过程称为上下文切换。如果在时间片用完之前,进程被阻塞,CPU 会立即进行切换,无需等待时间片用完。

举个例子,在一个多任务系统中,如果有三个进程A、B和C,假设时间片的大小为50毫秒。每个进程将轮流使用CPU,每次最多50毫秒。操作系统会按照以下顺序执行:

  • 进程A运行50毫秒。
  • 进程B运行50毫秒。
  • 进程C运行50毫秒。
  • 回到进程A,继续执行下一个时间片。

当一个进程被暂停时,操作系统会保存该进程的当前状态(进程控制块PCB)。下次切换回来时,操作系统会根据之前保存的状态恢复进程,继续执行。

虽然从宏观上看,操作系统似乎能够在同一时间段内执行多个任务,实际上,对于单核 CPU 来说,在任意时刻,只会有一个进程占用着 CPU 资源,这称之为并发,与并发这个词还有一个相似的概念叫做并行。

并发和并行

在计算机中,并发意味着多个任务在同一时间段内看似同时执行,但实际上它们是通过快速切换来共享 CPU 时间的。虽然每个任务并不是真正同时进行的,但如果切换得够快,给用户的感觉就像是多个任务同时在运行。

并发就像一个人(单核 CPU)在同时处理多件事情。想象你是一个服务员,负责照顾几张桌子上的客人。你可以走到第一桌,记下点的饮料,然后走到第二桌,记下点的主菜,再走到第三桌,记下甜点。虽然你在同一时刻只能服务一桌客人,但通过快速地在几桌之间切换,看起来好像你在同时为每桌提供服务。这就是并发。

并行意味着多个任务在不同的处理器(或 CPU 核心)上同时执行。因为每个任务都有自己独立的计算资源,所以它们是真正意义上地同时进行。

并行就像有多个服务员(多核 CPU)同时在不同的桌子上服务客人。每个服务员负责一张或几张桌子,他们可以真正同时接单、上菜、收盘子。这样,每张桌子都能独立得到服务,而不需要等待其他桌子的服务结束。这就是并行。

线程

尽管进程的引入显著提升了操作系统的性能,但随着时间的推移,人们希望单个进程能够处理更多的任务。如果一个进程中的多个子任务只能逐一执行,还是有很大的缺陷。

举个例子,当你使用浏览器浏览网页时,看到一个感兴趣的文件想要下载,如果在下载文件的过程中,浏览器无法继续加载其他网页,这显然是一个不好的用户体验。

为了解决这个问题,人们又引入了线程的概念。线程是进程中的一个执行单元,每个线程可以独立地完成一个子任务。这样,一个进程可以包含多个线程,如果需要,多个线程可以并发地执行。

例如,下面的 Java 代码展示了如何在主线程中启动两个新线程,每个线程负责执行不同的任务,一个线程负责打印 “hello world”,另一个线程负责打印 “I love you”:

class SayHelloThread extends Thread {
    public void run() {
        System.out.println("Hello world");
    }
}

class SayLoveThread extends Thread {
    public void run() {
        System.out.println("I love you");
    }
}

public class MultiThreadJavaApp {

    public static void main(String[] args) throws InterruptedException {
        SayHelloThread sayHelloThread = new SayHelloThread();
        SayLoveThread sayLoveThread = new SayLoveThread();
        sayHelloThread.start();
        sayLoveThread.start();
        //main thread sleep
        Thread.sleep(5000);
    }
}

//输出:
hello world
I love you

有趣的是,上面的程序每次运行时,输出的结果可能并不相同。这是因为 sayHelloThread 不一定总是能先获得执行的机会,具体哪个线程先执行,取决于操作系统的调度算法。

引入线程后,任务管理变得更加灵活和高效。当你在浏览器中下载文件时,可以由一个专门负责下载的线程来处理这个任务,而用户继续浏览网页时,浏览器则会运行另一个负责页面加载的线程。通过时间片轮转调度,操作系统可以在这些线程之间快速切换,从而让用户感觉下载和浏览网页的操作是在同时进行的。

进程和线程之间的区别

进程和线程的引入极大地提升了操作系统的性能,但它们之间到底有什么区别呢?

1. 资源占用

进程是操作系统分配资源的基本单位,包含程序执行的一个实例,包括代码、数据和系统资源(例如内存、文件、设备等)。每个进程都有独立的内存空间和系统资源,互不干扰。

线程是CPU调度的基本单位,多个线程共享同一个进程的内存空间和系统资源,但是每个线程拥有独立的栈、寄存器和程序计数器

2. 数据交换

进程之间是相互独立的,每个进程有自己的地址空间和系统资源。因此,进程之间的数据交换必须通过进程间通信(IPC)机制来实现,例如管道、消息队列、共享内存等,这种方式比较复杂。

线程是同一进程内的不同执行路径,共享同一个进程的内存空间和系统资源,因此线程之间的数据交换更加简单和快捷。

3. 开销

进程由于有独立的内存空间和系统资源,创建和销毁进程需要较大的开销,包括分配内存、加载程序、保存和恢复上下文信息等操作。

线程共享进程的内存空间和系统资源,创建和销毁线程的开销要小得多,仅需要保存和恢复少量的上下文信息,因此线程的切换成本较低。

4. 并发性

进程是独立的执行单元,拥有各自的调度算法,在并发执行时更加稳定可靠,因为一个进程的问题通常不会直接影响其他进程。

线程由于共享同一进程的资源,调度和同步相对复杂,需要仔细处理共享数据的并发访问,避免出现数据不一致或竞争条件等问题。这也是我们在后续文章中会深入讨论的一个关键点。

基于上面的区别,我们可以看到,在一个进程内实现多个任务的并发,最合适的方法是使用多线程,而不是多进程。不过,需要特别注意处理好并发逻辑,防止线程间的数据竞争和同步问题。

然而,这并不意味着多线程一定比多进程更优。在 Linux 系统中,创建进程的开销相对较小,因此 Linux 系统鼓励更多地使用多进程。但是,多进程的一个常见问题是进程间通信比较复杂和不方便,因此在 Linux 中,学习的重点之一就是掌握各种进程间通信(IPC)的方法。

简单来说,进程的引入使得操作系统可以实现多个程序的并发运行,而线程的引入则使得一个进程内部可以并发执行多个任务。

上下文切换

上下文切换(有时称为进程切换或任务切换)是指 CPU 从一个进程(或线程)切换到另一个进程(或线程)的过程。这里的“上下文”指的是 CPU 在某个时间点的所有状态信息,包括寄存器和程序计数器的内容。

寄存器是 CPU 内部少量的高速存储器,用于保存和访问运算过程中的中间值,从而提高计算速度。

程序计数器是一个专用的寄存器,用来指示 CPU 当前正在执行的指令(或将要执行的下一条指令)在指令序列中的位置。它存储的值可以是正在执行的指令的位置,也可以是下一条即将执行的指令位置,这取决于具体的系统实现。

CPU 通过为每个线程分配一定的时间片来实现多线程机制,并使用时间片轮转调度算法来执行任务。当一个线程执行完一个时间片后,CPU 就会切换到下一个线程。在切换前,CPU 会保存当前线程的状态信息,以便在将来切换回这个线程时能够恢复到之前的状态。

这个过程,也就是从保存当前任务状态到重新加载另一个任务状态的过程,就称为上下文切换。

假设当前线程 A 的时间片用完,需要切换到线程 B,操作系统会进行以下步骤:

  1. 暂停线程 A,将其在 CPU 中的状态(如寄存器、程序计数器等)保存到内存中。
  2. 从内存中加载线程 B 的上下文信息,并将这些信息恢复到 CPU 的寄存器中,以便执行线程 B。
  3. CPU根据程序计数器(此时保存的是将要执行的线程B的指令位置)的指示执行线程B的代码。

上下文切换通常是一个计算密集型操作,需要消耗大量的 CPU 时间。因此,线程数量增加并不总是能提高性能。如何减少系统中的上下文切换次数,是提升多线程性能的一个关键问题,我们将在后续的文章中进一步探讨。

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

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

相关文章

Swagger UI 无法发送 Cookie

文章目录 背景分析解决参考背景 项目中使用 Cookie 传递用户唯一标识,并在 Swagger 中添加 Cookie 作为全局请求参数。 尽管后端配置了 Cookie 请求参数,但在 Swagger UI 中使用 Try it out 发起请求时,发现请求中并没有 Cookie 传递过去。😭 首先,能看到 Curl 上已经有…

高级编程语言翻译例题

编译器的流程 源程序—词法分析—语法分析—语义分析—中间代码生成—代码优化—目标代码生成—目标程序 选项A:先进性词法分析,接着进行语法分析,最后进行语义分析 选项B:语法分析阶段只能发现程序上的语法错误,其…

软考高项通过率最高?!证书价值大吗?什么时候能报考?

近期有省份公布了2024年上半年软考合格人员名单,不少人说软考高项通过率最高,导致一些人十分想报名软考高项,从而拿证书。 那么,软考高项证书价值大吗?什么时候能报考? 1、通过率分析 在浙江公布的2024年…

YOLOv9改进策略【注意力机制篇】| GAM全局注意力机制: 保留信息以增强通道与空间的相互作用

一、本文介绍 本文记录的是基于GAM注意力模块的YOLOv9目标检测改进方法研究。GAM注意力模块通过3D排列和重新设计的子模块,能够在通道和空间方面保留信息,避免了先前方法中由于信息减少和维度分离而导致的全局空间-通道交互丢失的问题。本文利用GAM改进…

UML的图及其他图补充

一、UML图 1.类图 ‌类图‌是统一建模语言(UML)中的一种静态结构图,主要用于描述软件系统的静态结构。它显示了模型中的类、类的内部结构以及它们与其他类的关系。类图是面向对象建模的主要组成部分,用于对系统的词汇进行建模、对…

android开发---Kotlin语言基础语法

目录 数据打印 变量 函数 程序逻辑控制 if when 循环 数据打印 IDE采用的androidStudio 可自行官网下载 https://developer.android.google.cn/studio/archive?hlzh-cn 新建项目 添加一个main方法,main()函数的左边出现了一个运行标志的小箭头。现在我们只…

LLaMA-Factory仓基础功能架构及NPU/GPU环境实战演练

LLaMA-Factory 基础篇 LLaMA-Factory简介 LLaMA-Factory是一个开源的大规模语言模型微调框架,设计用于简化大模型的训练过程。它提供了一个统一的平台,支持多种大模型的微调,包括LLaMA、BLOOM、Mistral等,旨在帮助用户快速适应和…

数据脱敏处理

有关于数据脱敏处理,小编也是在文章上面看到的,感觉很有意思,那么,便深入研究了一下,首先我们先来看一下数据脱敏之后的结果吧? 用结果说话更能深入人心!! 下面是数据库中的字段&a…

fantastic-admin前端+django后端,初始化全流程记录

fantastic-admin前端是我目前看到最完善的前端框架,只需要简单的设置就可以快速开始项目。 但是我本人的能力有限,对前端知识一知半解,之前废了九牛二虎之力才跑通了前后端流程,由于新的项目需要,有了开发新后台的想法…

了解PD快充协议和QC快充协议

PD快充协议的实现依赖充电器与设备之间的通信协议,这种通信协议确保了充电器能够提供设备所需要的特定电压和电流。在快充技术中快充协议起到关键角色。 现在市面上最常见的快充协议有PD、QC、华为FCP/SCP、三星AFC协议 、VOOC闪充。PD和QC 协议属于公用协议 。华…

C/C++经典排序问题,sort函数使用

目录 1. 前言 2. 正文 2.1 问题 2.2 解决办法 2.2.1 思路 2.2.2 代码实现 2.2.3 测试结果 3. 备注 1. 前言 大家在学习C语言的时候,是不是经常被排序算法折磨的很难那首,其实都是但是在C中有专门的,排序函数,而且支持自定…

vue系统获取授权平台授权码实现单点登录、注销功能

公司平台需要对接别的平台 实现单点登录 注销。简而言之,不需要在自己公司系统登录 统一在别的平台登录后获取到登录凭证(授权码) 在本公司系统实现免密登录的功能。 流程: 跳转授权页面和保存授权码的代码: hrefLog…

模型融合创新性好强!最新成果直接登顶SOTA,分分钟拿下顶会

Transformer作者创业新成果火了!他们提出了一个70亿参数的日语数学大模型,直接打败700亿参数的Llama-2取得SOTA!更牛的是,得出这样的模型无需任何梯度训练,所需计算资源大大减少。 这种炸裂的成果得益于模型融合&…

C++模板(初识)

一、泛型编程 我们平时写交换函数的时候,会这样写: //交换两个int类型变量 void Swap(int& left, int& right) {int temp left;left right;right temp; } //交换两个double类型变量 void Swap(double& left, double& right) {doubl…

随着人们网络安全意识提高,软件架构设计与评估也成为重中之重

目录 案例 【题目】 【问题 1】(13 分) 【问题 2】(12分) 【答案】 【问题 1】答案 【问题 2】答案 相关推荐 案例 阅读以下关于软件架构设计与评估的叙述,回答问题 1 和问题 2。 【题目】 某电子商务公司为正更好地管理用户,提升企业销售业绩&…

Linux中Ubuntu系统安装Windows得字体

背景 安装了geoserver 然后geoserver中需要用到微软雅黑字体 所以需要安装一下Linux系统安装Windows中的字体 创建字体目录 cd /usr/share/fonts/ mkdir winfont在Windows找到对应字体 C:\Windows\Fonts 复制该字体到桌面 Linux系统中上传字体 roottest-server03:/usr/sha…

一键解决物流追踪难题:批量查询工具助力电商运营

探索固乔科技,解锁高效物流查询新纪元!固乔快递批量查询助手,一款专为电商、物流从业者及自媒体人打造的神器,让繁琐的物流追踪变得轻松快捷。 想象一下,万级单号批量导入,仅需5分钟,所有物流动…

如何利用mHand Pro动捕数据手套连接虚拟与现实?

数据手套作为虚拟现实中的一种交互动捕设备,能够模拟真人手部的动作和感知反馈,实现人机交互的效果。随着虚拟现实技术的不断发展,数据手套也在不断地改进和升级。 mHand Pro是一款由拥有多年经验的惯性动作捕捉技术团队广州虚拟动力研发的数…

第142天: 内网安全-权限维持黄金白银票据隐藏账户C2 远控RustDeskGotoHTTP

案例一: 内网域&单机版-权限维持-基于用户-隐藏用户 项目下载地址: GitHub - wgpsec/CreateHiddenAccount: A tool for creating hidden accounts using the registry || 一个使用注册表创建隐藏帐户的工具 用这个工具的话在域中会把它加入adminis…

会声会影哪个版本最好用?

会声会影哪个版本最好用? 会声会影2023这个版本是最受欢迎的,它为多数用户提供了稳定且强大的功能。以下是关于为什么这个版本最好用的 一、功能丰富 会声会影X系列版本拥有从视频剪辑、音频编辑到特效添加等全方位的功能。用户可以轻松完成视频的录制、剪辑、转…