Linux线程【1概念】

news2024/9/21 0:32:13

 

目录

前言:

正文:

1.什么是线程?

1.1基本概念

1.2线程理解 

1.3进程与线程的关系

1.4线程使用

2.重谈地址空间 

 2.1页表的大小

2.2、内存与磁盘的交互

2.3深入页表 

2.4小结

3.线程概念总结 

3.1、再谈线程

3.2线程的优点 

 3.3线程缺点

3.4线程用途 



前言:

一份代码成功编译后,成为可执行程序,运行起来,相关代码和数据被加载到内存中,操作系统为了管理进程,会先描述在组织,难道只有进程能够完成用户想要执行的任务吗?如果任务很大呢?为了满足高效运行的需求,需要一种执行力度更细的,成本更低的执行流,在linux操作系统中将这样的执行流称之为-线程 

 windos 中线程数据

 

正文:

1.什么是线程?

1.1基本概念

教材 概念:

  1. 线程就是一个执行分支、执行粒度比进程更细、调度成本更低
  2. 线程就是进程内部的一个执行流

内核角度:

  • 进程是承担系统资源分配的基本实体,而线程是 CPU 运行的基本单位

线程是对以往进程概念的补充完善,正确理解线程概念是一件十分重要的事

1.2线程理解 

理解 线程 之前需要先简单回顾一下 进程

  • 程序运行后,相关的代码和数据会被 load 到内存中,然后操作系统为其创建对应的 PCB 数据结构、生成虚拟地址空间、分配对应的资源,并通过页表建立映射关系

进程之间是相互独立

即使是 父子进程,他们也有各自的 虚拟地址空间、映射关系、代码和数据(可能共享部分数据,出现修改行为时引发 写时拷贝机制)

如果我们想要创建 其他进程 执行任务,那么 虚拟地址空间、映射关系、代码和数据 这几样东西是必不可少的,想象一下:如果只有进程的概念,并且同时存在几百个进程,那么操作系统调度就会变得十分臃肿

 

  • 对于频繁IO操作,系统在调度进程时,需要频繁保存上下文数据、创建的虚拟地址空间及建立映射关系

为了避免这种繁琐的操作,引入了 线程 的概念,所谓 线程 就是:额外创建一个 task_struct 结构,该 task_struct 同样指向当前的虚拟地址空间,并且不需要建立映射关系及加载代码和数据,如此一来,操作系统只需要 针对一个 task_struct 结构即可完成调度,成本非常低

为什么切换进程比切换线程开销大得多?
在 CPU 内部包括:运算器、控制器、寄存器、MMU、硬件级缓存(cache),其中 硬件级缓存 cache 又称为 高速缓存,遵循计算机设计的基本原则:局部性原理,会预先加载 部分用户可能访问的数据 以提高效率,如果切换进程,会导致 高速缓存 中的数据无法使用(进程具有独立性),重新开始 预加载,这是非常浪费时间的(对于 CPU 来说);但切换线程就不一样了,因此线程从属于进程,切换线程时,所需要的数据的不会发生改变,这就意味值 高数缓存 中的数据可以继续使用,并且可以接着 预加载 下一波数据

 注:高速缓存中预加载的是公共数据,并非线程的私有数据

 

进程(process)的 task_struct 称为 PCB,线程(thread)的 task_struct 则称为 TCB

从今天开始,无论是 进程 还是 线程,都可以称为 执行流,线程 从属于 进程:当进程中只有一个线程时,我们可以粗粒度的称当前进程为一个单独的执行流;当进程中有多个线程时,则称当前进程为多执行流,其中每一个执行流就是一个个的线程

执行流的调度由操作系统负责,CPU 只负责根据 task_struct 结构进行计算

  • 若下一个待调度的执行流为一个单独的进程,操作系统仍需创建 PCB 及 虚拟地址空间、建立映射关系、加载代码和数据
  • 但如果下一个待调度的执行流为一个线程,操作系统只需要创建一个 TCB,并将其指向已有的虚拟地址空间即可

1.3进程与线程的关系

        进程是承担系统资源分配的实体,比如 程序运行必备的:虚拟地址空间、页表映射关系、相关数据和代码 这些都是存储在 进程 中的,也就是我们历史学习中 进程 的基本概念

        线程是 CPU 运行的基本单位,程序运行时,CPU 只认 task_struct 结构,并不关心你是 线程 还是 进程,不过,线程 包含于 进程 中,一个 进程 可以只有一个 线程,也可以有很多 线程,当只有一个 线程 时,通常将其称为 进程,但对于 CPU 来说,这个 进程 本质上仍然是 线程;因为 CPU 只认 task_struct 结构,并且 PCB 与 TCB 都属于 task_strcut,所以才说 线程是 CPU 运行的基本单位

总结:进程是由操作系统将程序运行所需地址空间、映射关系、代码和数据打包后的资源包,而 线程/轻量级线程/执行流 则是利用资源完成任务的基本单位

线程包含于进程中,进程本身也是一个线程

我们之前学习的进程概念是不完整的,引入线程之后,可以对进程有一个更加全面的认识

通常将程序启动,比如 main 函数中的这个线程称为 主线程,其他线程则称为 次线程

实际上一个完整的进程 = 内核PCB+虚拟地址空间+映射关系+代码和数据,同时线程又分割进程除了映射代码和数据以外的部分

 

 

在 Linux 中,认为 PCB 与 TCB 的共同点太多了,于是直接复用了 PCB 的设计思想和调度策略,在进行 线程管理 时,完全可以复用 进程管理 的解决方案(代码和结构),这可以大大减少系统调度时的开销,做到 小而美,因此 Linux 中实际是没有真正的 线程 概念的,有的只是复用 PCB 设计思想的 TCB

在这种设计思想下,线程 注定不会过于庞大,因此 Linux 中的 线程 又可以称为 轻量级进程(LWP),轻量级进程 足够简单,且 易于维护、效率更高、安全性更强,可以使得 Linux 系统不间断的运行程序,不会轻易 崩溃

与 一切皆文件一样,这种设计思想注定 Linux 会成为一款 卓越 的操作系通

1.4线程使用

如何验证 Linux 中的线程解决方案? 简单使用一下就好了

接下来简单使用一下 pthread 线程原生库中的线程相关函数(只是简单使用,不涉及其他操作)

#include <iostream>
#include <unistd.h>
#include <pthread.h>

using namespace std;

void *threadHandler1(void *args)
{
    while (true)
    {
        cout << "我是次线程1,我正在运行..." << endl;
        sleep(1);
    }
}

void *threadHandler2(void *args)
{
    while (true)
    {
        cout << "我是次线程2,我正在运行..." << endl;
        sleep(1);
    }
}

void *threadHandler3(void *args)
{
    while (true)
    {
        cout << "我是次线程3,我正在运行..." << endl;
        sleep(1);
    }
}

int main()
{
    pthread_t t1, t2, t3; // 创建三个线程
    pthread_create(&t1, NULL, threadHandler1, NULL);
    pthread_create(&t2, NULL, threadHandler2, NULL);
    pthread_create(&t3, NULL, threadHandler3, NULL);

    // 主线程运行
    while (true)
    {
        cout << "我是主线程" << endl;
        sleep(1);
    }

    return 0;
}

编译程序时,需要带上 -lpthread 指明使用 线程原生库

结果:主线程+三个次线程同时在运行

至于为什么打印结果会有点不符合预期,这就涉及到 加锁 相关问题了,后面再解决

使用指令查看当前系统中正在运行的 线程 信息

ps -aL | head -1 && ps -aL | grep myThread | grep -v grep

 

可以看到此时有 四个线程

  • 细节1:四个线程的 PID 都是 13039
  • 细节2:四个线程的 LWP 各不相同
  • 细节3:第一个线程的 PID 和 LWP 是一样的

其中,第一个线程就是 主线程,也就是我们之前一直很熟悉的 进程,因为它的 PID 和 LWP 是一样的,所以只需要关心 PID 也行

操作系统如何判断调度时,是切换为 线程 还是切换为 进程 ?

  • 将待切换的执行流 PID 与当前执行流的 PID 进行比对,如果相同,说明接下来要切换的是 线程,否则切换的就是 进程
  • 操作系统只需要找到 LWP 与 PID 相同的线程,即可轻松锁定主线程

线程是进程的一部分,给其中任何一个线程发送信号,都会影响到其他线程,进而影响到整个进程


2.重谈地址空间 

 2.1页表的大小

 页表 是用来将 虚拟地址 和 物理地址 之间建立映射关系的,除此之外,页表 中还存在 其他属性 字段

众所周知,在 32 位系统中,存在 2^32 个地址(一个内存单元大小是 1byte),意味着虚拟地址空间 的大小为 4GB

假设极端情况:每个地址都在页表中建立了映射关系,其中页表的每一列大小都是 4 字节,那么页表的大小就是 2^32 * 4 * 3 * 1byte = 48GB,这就意味着悲观情况下页表已经干掉 48GB 的内存了,但现在电脑普遍都只有 16GB 内存,更何况是几十年前的电脑

所以页表的地址映射肯定不是这样的。

2.2、内存与磁盘的交互


操作系统从 磁盘 中读取数据时,一次读取大量数据 比 多次读取少量数据 要快的多,因为 磁盘 是外设,每一次读取都必然伴随着寻址等机械运动(机械硬盘),无论是对于 内存 还是 CPU ,这都是非常慢的,为了尽可能提高效率,操作系统选择一次 IO 大量数据的方式读取数据

通常 IO 的数据以 块 为基本单位,在文件系统中,一个 块 的大小为 4KB(一个块由8个扇区组成,单个扇区大小为 512Byte),即使我们一次只想获取一个字节,操作系统最低也会 IO 一个 数据块(4KB)

4KB 这个大小很关键

  • 文件系统/编译器:文件存储时,需要以 4KB 为单位进行存储
  • 操作系统/内存:读取文件或进行内存管理时,也是以 4KB 为单位的

也就是说,内存实际上是被切成大小为 4KB 的小块的,在内存中,单块内存(4KB)被称为 页 Page,组成单块内存的边界(类似于下标)被称为 页框(页帧)

为了将内存中的 页 Page 进行管理,需要 先描述,在组织,构建 struct page 结构体,用于描述 页 Page 的状态,比如是否为脏数据、是否已经被占用了,因为存在很多 页 Page,所以需要将这些 struct page 结构进行管理,使用的就是 数组(天然有下标) struct page mem[N],其中 N 表示当前内存中的 页 Page 数量

struct page
{
	int status; // 基础字段:状态
	// 注意:这个结构不能设计的太复杂了,因为稍微大一点内存就爆了,所以里面的属性非常少
};

struct page mem[N]; // 管理 page 结构体的数组

 

假设我们的内存为 4GB,那么等分为 4KB 的 页 Page,可以得到约 100w 个 页 Page,其中 struct page 结构体不会设计的很大,大小是 字节 级别的,也就是说 struct page mem[100w] 占用的总大小不过 4~5MB,对于偌大的内存来说可以忽略不计

内存管理的本质:

  • 申请:无非就是寻找 mem 数组中一块未被使用的足量空间,将对应的 页 Page 属性设置为已被申请,并返回起始地址(足量空间页框的起始地址)
  • 使用:将磁盘中的指定的 4KB 大小数据块存储至内存中对应的 页 Page 中
  • 释放:将 页 Page 属性设置为可用状态

关于 mem 数组的查找算法(内存分配算法):LRU、伙伴系统等

重新审视 4KB,为什么内存与磁盘交互的基本单位是 块(4KB

这里就要提一下 局部性原理 了

  • 现代计算机预加载的理论基础
  • 允许我们提前加载正在访问数据的 相邻或者附加的数据(数据预加载)

所以现在就可以回答为什么是 4KB

  1. IO 的基本单位,内核系统/文件系统 都对其提供了支持
  2. 利于通过 局部性原理 预测数据的命中情况,尽可能提高效率

总结:IO 的基本单位是 4KB,内存实际上被划分成了很多个 4KB 的小块,并存在相应的数据结构对其进行管理

2.3深入页表 

 

虚拟地址(32 位操作系统) 大小也就是 32 比特位,大概也就是 4Byte,通常将一个 虚拟地址 分割为三份:10、10、12

10:虚拟地址中的前 10 个比特位,用于寻址 页表2
10:虚拟地址中间的 10 个比特位,用于寻找 页框起始地址
12:虚拟地址中的后 12 个比特位,用于定位 具体地址(偏移量)

注:“页表2” 中的 20 表示内存中的下标,即 页框地址

通常将 “页表1” 称为 页目录,“页表2” 称为 页表项

  • 页目录:使用 10 个比特位定位 页表项
  • 页表项:使用 10 个比特位定位 页框地址
  • 偏移量:使用 12 个比特位,在 页 Page 中进行任意地址的寻址

像这种 页框起始地址+偏移量 的方式称为 基地址+偏移量,是一种运用十分广泛的思想,比如所谓的 类型(int、double、char…)都是通过 类型的起始地址+类型的大小 来标识该变量大小的,也就是说我们只需要 获得变量的起始地址,即可自由进行偏移操作(如果偏移过度了,就是越界),这也就解释了为什么取地址只会取到 起始地址

扩展:动态内存管理

实际上,我们在进行 动态内存管理(malloc/new) 申请堆空间时,操作系统 并没有立即在物理内存中申请空间(因为你申请了可能不会立马使用),而是 先在 虚拟地址 中进行申请(成本很低),当我们实际使用该空间时,操作系统 再去 填充相应的页表信息+申请具体的物理内存

像这种操作系统赌博式的行为我们已经不是第一次见了,比如之前的 写时拷贝,就是在赌你不会修改,这样做的好处就是可以 最大化提高效率,对于内存来说,这种使用时再申请的行为会引发 缺页中断

 

同理,在进行 磁盘文件读取 时,也存在 缺页中断 行为,毕竟你打开文件了,并不是立即进行读写操作的

诸如这种 硬件级的中断行为 我们已经在 信号产生 中学过了,即:从键盘按下的那一刻,发出硬件中断信号,中断控制器识别为 键盘 发出的信号后,去 中断向量表 中查找执行方法,也就是 键盘 的读取方法

所以操作系统根本不需要关系 硬件 是什么样子,只需要关心对方是否发出了 信号(请求),并作出相应的 动作(执行方法) 即可,很好的实现了 解耦

对于 内存 的具体情况,诸如:是否命中、是否被占用、对应的 RWX 权限 需要额外的空间对其进行描述,而 页表 中的 其他属性 列就包含了这些信息

 

2.4小结

 所以目前 地址空间 的所有组成部分我们都已经打通了,再次回顾这种设计时,会发现 用户压根不知道、也不需要知道虚拟地址空间之后发生的事,只需要正常使用就好了,当引发异常操作时,操作系统能在 查页表 阶段就进行拦截,而不是等到真正影响到 物理内存 时才报错

这种设计思想就是计算机界著名的 所有问题都可以通过添加一层 软件层 解决,这种思想早在几十年前就已经得到了运用

这种分层结构不仅适用于 操作系统,还适用于 网络,比如大名鼎鼎的 OSI 七层网络模型

3.线程概念总结 

3.1、再谈线程


Linux 中没有 真线程,有的只是复刻 进程 代码和管理逻辑的 轻量级线程(LWP)

线程 有以下概念:

在一个程序中的一个执行路线就叫做 线程(Thread),或者说 线程 是一个进程内部的控制程序
每一个进程都至少包含一个 主线程
线程 在进程内部执行,本质上仍然是在进程地址空间内运行
在 Linux 系统中,CPU 看到的 线程 TCB 比传统的 进程 PCB 更加轻量化
透过进程地址空间,可以看到进程的大部分资源,将资源合理分配给每个执行流,就形成了 线程执行流

3.2线程的优点 

线程 最大的优点就是 轻巧、灵活,更容易进行调度

创建一个线程的代价比创建一个进程的代价要小得多
调度线程比调度进程要容易得多
线程占用的系统资源远小于进程
可以充分利用多处理器的并行数量(进程也可以)
在等待慢速 IO 操作时,程序可以执行其他任务(比如看剧软件中的 “边下边看” 功能)
对于计算密集型应用,可以将计算分解到多个线程中实现(比如 压缩/解压 时涉及大量计算)
对于 IO密集型应用,为了提高性能,将 IO操作重叠,线程可以同时等待资源,进行 高效IO(比如 文件/网络 的大量 IO 需要,可以通过 多路转接 技术,提高效率)

 3.3线程缺点

 

1、性能损失,当 线程 数量过多时,频繁的 线程 调度所造成的消耗会导致 计算密集型应用 无法专心计算,从而造成性能损失

2、 健壮性降低,在一个多线程程序里,因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的,换句话说线程之间是缺乏保护的

为什么 单个线程 引发的错误需要让 整个进程 来承担?

站在技术角度,完全可以让其自行承担,但这不合理
系统角度:线程是进程的执行分支,线程出问题了,进程也不应该继续运行(比如一颗老鼠屎坏了一锅汤)
信号角度:线程出现异常后,MMU 识别到异常 -> 操作系统将异常转换为信号 -> 发送信号给指定进程,信号的对象是进程,自然无法单发给 线程,进而整个进程也就都终止了

3、缺乏访问控制,进程是访问控制的基本粒度,在一个线程中调用某些OS函数会对整个进程造成影响

3.4线程用途 

合理的使用 多线程,可以提高 CPU 计算密集型程序的效率

合理的使用 多线程,可以提高 IO 密集型程序中用户的体验(具体表现为用户可以一边下载,一边做其他事情)

 

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

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

相关文章

MYSQL 解释器小记

解释器的结果通常通过上述表格展示&#xff1a; 1. select_type 表示查询的类型 simple: 表示简单的选择查询&#xff0c;没有子查询或连接操作 primary:表示主查询&#xff0c;通常是最外层的查询 subquery :表示子查询&#xff0c;在主查询中嵌套的查询 derived: 表示派…

pip降级在pycharm中

PyCharm依赖于"–build-dir"参数安装第三方库&#xff0c;但该参数在最新的23.0版pip中已删除 解决办法就是降级pip&#xff0c;PyCharm中选择File&#xff0c;找到编译器&#xff0c;点击pip&#xff0c;勾选对应版本即可 或者在cmd中执行运行python -m pip install…

android开发软件下载,2024年Android开发陷入饱和

谈起性能优化&#xff0c;我想问&#xff0c; 什么是性能优化&#xff1f;你的理解呢&#xff1f; **首先什么是性能&#xff1a;**在同一个手机里面&#xff0c;同样功能的app&#xff0c;哪个跑的快&#xff0c;哪个不卡&#xff0c;哪个就性能高。我们这篇文章就是解决那些…

SQL注入漏洞解析--less-6

1.第六关了。 2.这个和第五关有点像&#xff0c;只是换成了双引号&#xff0c;接下来的都一样&#xff0c;看我操作(换个函数试一下extractvalue&#xff0c;他的报错位置在第二个&#xff0c;那我就利用一下) 3.爆库名 ?id1"%20and%20extractvalue(1,concat(0x7e,(sele…

后地产时代,卫浴品牌有何变革之道?

联系与卫浴密切相关的房地产行业现状来看&#xff0c;卫浴行业正处于变革期。 国家统计局数据显示&#xff0c;2023年&#xff0c;商品房销售面积111735万平方米&#xff0c;比上年下降8.5%&#xff0c;其中住宅销售面积下降8.2%&#xff1b;商品房销售额116622亿元&#xff0…

低功耗微处理器复位检测电路,工作温度范围-40~+80℃,可与μP监控产品兼容——D706

近年来&#xff0c;微处理器在IT业控制领域和智能化产品中得到了广泛的应用。在系统和产品的开发设计过程中&#xff0c;为了提高其抗干扰能力&#xff0c;使用μP监控是首选技术措施之一。监控芯片可为系统提供上电、掉电复位功能&#xff0c;也可提供其它功能&#xff0c;如后…

二分通用模板+例题java

&#x1f9e1;&#x1f9e1;二分大前提&#x1f9e1;&#x1f9e1; 有序数列&#xff01;&#xff01; &#x1f9e1;&#x1f9e1;模板&#x1f9e1;&#x1f9e1; 查找具体某个元素 while(left<right) &#xff1a;三个分支&#xff0c;在循环里直接返回答案 //二分查找具…

JSON 文件里的 “$schema” 是干什么用的?

最近我在做一些前端项目&#xff0c;我发现有的配置文件&#xff0c;比如 .prettierrc.json 或者 tsconfig.json 里面都会看到一个 $schema 字段&#xff0c;有点好奇&#xff0c;就查了一下。 什么是 JSON Schema JSON Schema是一种基于JSON (JavaScript Object Notation) 的…

电梯物联网之梯控相机方案-防止电瓶车进电梯

梯控现状 随着电梯产品在智能化建筑的日益普及,对于电梯的智能化管理 安全性需求 的要求越来越迫切。尤其今年来随着电瓶车的大量普及&#xff0c;发起多起楼道、轿厢电瓶车着火恶性事件&#xff0c; 造成了极大的社会 负面影响。控制电瓶车进入单元门&#xff0c;楼道以及电梯…

浅析智能照明控制系统在图书馆照明节能中的应用

彭姝麟 Acrelpsl 0 引 言 照明耗电在各国总发电量中都占有很大比例&#xff0c;根据2004年国家住房和城乡建设部的统计&#xff0c; 我国照明耗电约占全国发电总量的10&#xff05;一12&#xff05;。 对一些照明时间较长、照明场所较多的机构&#xff0c;如高等学校&#xf…

百度新版VMP

百度搞事情哇最近&#xff0c;新上验证类型&#xff0c;曲线匹配&#xff01;&#xff01;&#xff01; 这年头儿&#xff0c;连百度都还是上vmp了兄弟们&#xff0c;越来越难咯。 个人感觉 想学好vm的话&#xff0c;首先得找个好位置插装&#xff0c;然后慢慢分析。 轨迹信息…

Spring注解之json 数据处理

目录 1. 过滤 json 数据 2. 格式化 json 数据 3. 扁平化对象 1. 过滤 json 数据 JsonIgnoreProperties 作用在类上用于过滤掉特定字段不返回或者不解析。 //生成json时将userRoles属性过滤 JsonIgnoreProperties({"userRoles"}) public class User { ​private S…

C++的继承和多态

继承和多态 继承继承的权限继承的子父类访问派生类的默认成员函数菱形继承&#xff08;C独有&#xff09;【了解】虚拟继承什么是菱形继承&#xff1f;菱形继承的问题是什么&#xff1f;什么是菱形虚拟继承&#xff1f;如何解决数据冗余和二义性的继承和组合的区别&#xff1f;…

Redis冲冲冲——事务支持,AOF和RDB持久化

目录 引出Redis事务支持&#xff0c;AOF和RDB持久化1、Redis的事务支持2、Redis的持久化 Redis冲冲冲——缓存三兄弟&#xff1a;缓存击穿、穿透、雪崩缓存击穿缓存穿透缓存雪崩 总结 引出 Redis冲冲冲——事务支持&#xff0c;AOF和RDB持久化 Redis事务支持&#xff0c;AOF和…

Windows系统x86机器安装龙芯(loongarch64)3A5000虚拟机系统详细教程

本次介绍在window系统x86机器上安装loongarch64系统的详细教程。 1.安装环境准备。 首先&#xff0c;你得有台电脑。 配置别太差&#xff0c;至少4核8G内存&#xff0c;安装window10或者11都行&#xff08;为啥不能是Window7&#xff0c;你要用也不是不行&#xff0c;你先解决…

模拟算法题练习(二)(DNA序列修正、无尽的石头)

&#xff08;一、DNA序列修正&#xff09; 问题描述 在生物学中&#xff0c;DNA序列的相似性常被用来研究物种间的亲缘关系。现在我们有两条 DNA序列&#xff0c;每条序列由 A、C、G、T 四种字符组成&#xff0c;长度相同。但是现在我们记录的 DNA序列存在错误&#xff0c;为了…

解析电源模块测试条件与测试步骤 快速完成测试

高温高湿储存测试是电源模块环境适应性测试内容之一&#xff0c;在实际使用过程中由于应用场景不同电源所处的环境也是多样的&#xff0c;因此需要测试电源对各种环境的适应能力&#xff0c;提高电源的性能和可靠性。 电源高温高湿存储测试的目的是为了测量环境对电源结构、元件…

Spring - InitializingBean、@PostConstruct、@Bean(initMethod = “init“)和构造方法执行优先级比较

执行顺序优先级 构造方法 > postConstruct > afterPropertiesSet > init方法 代码案例 Component public class InitializingBeanTest implements InitializingBean {public InitializingBeanTest(){System.out.println("构造方法");}Overridepublic void…

现代企业架构框架——技术架构

现代企业架构框架——技术架构。 技术架构是对某一技术问题(需求)解决方案的结构化描述,由构成解决方案的组件结构及之间的交互关系构成。广义上的技术架构是一系列涵盖多类技术问题设计方案的统称,例如部署方案、存储方案、缓存方案、日志方案等等。 现代企业架构框架(M…

直观理解卷积

卷积直观理解 原文来自最容易理解的对卷积(convolution)的解释 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;计算机杂记 &#x1f380;CSDN主页 发狂的小花 &#x1f304;人生秘诀&#xff1a;学习的本质就是极致…