一、为什么操作系统需要线程?(线程的由来)

1. 进程的致命缺点

早期操作系统以进程为最小调度单位,但进程有非常大的局限性:

进程资源太重:每个进程独立内存、独立PC、独立地址空间(虚拟内存+页表),创建、销毁、切换开销极大。

进程切换慢:进程切换需要刷新页表、刷新缓存、保存大量寄存器,耗时高。

进程内部无法并行:一个进程同一时刻只能干一件事,阻塞时整个进程卡死,无法利用多核。

进程间通信比较麻烦:需要通过管道、虚拟内存、消息队列等才能实现进程间通信。

2. 线程的诞生目的

为了解决这些进程太重、切换慢、并发能力差 的问题,操作系统引入了线程(轻量级进程)。

核心思想:进程负责分配资源,线程负责干活。

线程共享进程大部分资源资源,创建、销毁、切换成本极低

一个进程可以包含多个线程,真正实现一个程序同时做多件事

极大提升 CPU 利用率、程序并发能力

打个比喻:如果将进程比作工厂中的一个车间,那么线程就是每个车间中进行的工人

二、线程的基本概念

1. 什么是线程?

线程是操作系统能够进行运算调度的最小单位,是进程内的一条执行路径。线程被称为是轻量级进程,在计算机看来进程就是一个PCB结构体,同样的线程就是一个和线程类似的TCB结构体。

一个进程至少有一个主线程,多个线程共享进程的:

        堆内存、全局变量、文件描述符、信号、地址空间

线程私有:

        线程栈、程序计数器、局部变量、寄存器状态

2. 线程的特点

        轻量级,开销小

        共享进程资源,通信简单

        切换速度远快于进程

        线程之间相互影响(一个线程崩了可能影响整个进程)

三、线程与进程的核心区别(重点)

这是面试最高频考点,我帮你整理成博客通俗版:

1. 资源层面

        进程:资源分配的最小单位,独立地址空间,互不干扰

        线程:CPU调度执行的最小单位,共享进程资源

2. 开销层面

        进程创建销毁慢、切换极慢

        线程创建销毁快、切换极快

3. 通信方式

        进程通信复杂:管道、消息队列、共享内存、socket

        线程通信简单:直接读写全局变量即可

4. 独立性

        进程完全独立,一个进程崩溃不影响其他进程

        线程不独立,一个线程异常崩溃,整个进程就会退出

四、并发与并行(彻底讲清楚区别)

1. 并发 

单核 CPU 快速交替执行多个线程

宏观看起来同时运行,微观上是在CPU中交替执行的。

作用:提高IO 密集型任务效率(等待IO时切换线程)

2. 并行

多核 CPU 同一时刻真正同时执行多个线程

真正意义上的同时运行。

作用:提高CPU 密集型任务效率(多核CPU)

3. 总结

并发是交替做,并行是同时做。

五、线程安全问题(为什么会出现线程不安全?)

1. 线程不安全根本原因

        多线程共享资源(全局变量、静态变量、堆对象)

        CPU 时间片随机抢占、线程交替执行

        读写操作不具备原子性

2. 经典案例:count++ 线程不安全

count++ 实际分为三步:

  1. 读取 count 值

  2. CPU 计算 +1

  3. 写回内存

多线程交替执行会导致覆盖写、数据丢失,最终结果偏小。

3. 线程安全的定义

多线程并发访问共享数据时,最终结果和单线程顺序执行结果一致,没有数据错乱、数据丢失。

六、线程的同步与互斥(核心重难点)

1. 互斥

(1)互斥是什么?

同一时刻只允许一个线程访问共享资源。

解决:竞争资源冲突、数据覆盖问题。

典型实现:互斥锁 mutex、synchronized

(2)如何实现互斥?

使用互斥锁或者信号量(将信号量的初始值设置为1,即可实现互斥的效果)

2. 同步

(1)同步是什么?

线程按照预定顺序、有序执行。

解决:线程执行顺序混乱、生产者消费者顺序问题。

(2)如何实现同步?

使用条件变量搭配互斥锁、信号量

互斥:竞争关系,抢资源,互斥排斥

同步:协作关系,互相等待、有序执行

七、死锁的概念、产生条件

1. 什么是死锁?

多个线程互相持有对方需要的锁,又互相等待对方释放锁,无限阻塞、程序卡死,无法继续执行。

2. 死锁产生的四大必要条件(必考)

互斥条件:资源同一时刻只能一个线程持有

持有并等待:线程持有旧锁,又请求新锁

不可剥夺:锁不能被强制抢走,只能主动释放

环路等待:线程之间形成环形锁等待链

四个条件同时满足才会死锁,破坏任意一个即可避免死锁。

八、死锁的预防、避免、解决方式

1. 死锁预防(从代码层面杜绝条件)

破坏请求与保持:一次性申请所有需要的锁

破坏环路等待:所有线程统一锁的获取顺序

破坏不可剥夺:加锁倒计时,超时放弃锁请求

2. 死锁避免(运行时动态规避)

经典算法:银行家算法

核心:每次分配资源前预判,如果预判的结果可以顺利执行,并不会发生死锁,就会进行资源分配,如果会发生死锁,则重新寻找新的资源分配的方案

3. 工程中常用解决方案

        统一锁的获取顺序(最常用、最简单)

        加锁超时机制

        将所有需要的锁一次性申请完

九、总结

线程的出现解决了进程过重、并发能力弱的问题,是现代高并发服务的基石。

但多线程带来三大核心问题:线程安全、同步互斥、死锁。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐