刚聊完“共享内存像一个没有秩序的菜市场,容易把数据写乱”,紧接着问“信号量”,你这属于精准踩中了操作系统的核心连环招!

一句话定义信号量(Semaphore):它就是操作系统的“红绿灯”和“计数器”,专门用来解决多进程/多线程在抢夺共享资源时的“交通冲突”问题。

为了让你彻底搞懂,我们先看它的底层机制,再看它在实际开发中的绝妙舞台。


1. 信号量的机制到底是怎么用的?

信号量的本质其实非常简单,它在内核里就是一个特殊的整型变量(我们假设叫 S,但是你不能直接去修改它,只能通过操作系统提供的两个著名的原子操作(学术上叫 P 操作V 操作)来动它:

  • P 操作(申请资源 / 踩刹车 / 变红灯): 每次进程想要用资源,先执行 P。变量 S 减 1。

  • 如果减完后 S ≥ 0 S \ge 0 S0,说明还有空位,放行!

  • 如果减完后 S < 0 S < 0 S<0,说明资源被占满了,这个进程直接被挂起(卡住、休眠),在队列里排队等待。

  • V 操作(释放资源 / 踩油门 / 变绿灯): 进程用完资源了,执行 V。变量 S 加 1。

  • 如果加完后 S ≤ 0 S \le 0 S0,说明排队队列里还有哥们在苦苦沉睡,操作系统就会立刻唤醒排在最前面的那个进程,让它进去爽。


2. 核心场景一:互斥锁(Binary Semaphore)—— “只能一个人进的洗手间”

互斥是信号量最经典的用法。这时候,信号量的值 S = 1(只有 1 个资源)。

🏢 现实场景:

两个进程(进程 A 和进程 B)通过共享内存同时去修改同一个银行账户的余额。如果不加控制,两个进程同时读到余额是 100,同时加 10,最终结果变成 110(正确应该是 120),这就是数据错乱。

🛠️ 信号量怎么救场:

  1. 我们设信号量 S = 1
  2. 进程 A 来了:执行 P 操作,S 变成 0。由于 S ≥ 0 S \ge 0 S0,进程 A 兴高采烈地进入共享内存去修改余额。
  3. 进程 B 这时也来了:执行 P 操作,S 变成 -1。由于 S < 0 S < 0 S<0,进程 B 当场被操作系统拍晕(阻塞休眠),卡在门口进不去。
  4. 进程 A 大功告成:退出共享内存,执行 V 操作,S 变成 0。因为 S ≤ 0 S \le 0 S0,操作系统把守在门口的进程 B 唤醒
  5. 进程 B 醒来:安全地进入共享内存修改余额,绝对不会和进程 A 撞车。

3. 核心场景二:同步(Synchronization)—— “必须等老子先干完”

同步是指两个进程之间有严格的先后顺序。比如:进程 B 的输入,必须依赖进程 A 的输出。

🏢 现实场景:

工厂流水线上,进程 A 负责“生产手机主板”,进程 B 负责“组装手机外壳”。如果主板还没造出来,进程 B 就绝对不能开工,否则就是组装空气。

🛠️ 信号量怎么救场:

  1. 我们初始化信号量 S = 0(代表默认没有任何现成的主板)。
  2. 进程 B(组装)猴急地先开工了:它执行 P 操作,S 瞬间变成 -1。因为 S < 0 S < 0 S<0,进程 B 乖乖地原地躺平休眠,等待主板。
  3. 进程 A(生产)慢吞吞地开工:花了一小时终于把主板造好了。
  4. 进程 A 执行 V 操作S 变成 0。因为 S ≤ 0 S \le 0 S0,操作系统立刻唤醒进程 B:“主板好了,你可以组装了!”

4. 核心场景三:资源计数(Counting Semaphore)—— “停车场看门老大爷”

这时候,信号量的值 S = N(代表有 N 个相同的资源)。

🏢 现实场景:

一个服务器的数据库连接池,最多只允许同时有 3 个线程连数据库。

🛠️ 信号量怎么救场:

  1. 初始化信号量 S = 3(3 个车位)。
  2. 线程 1、2、3 陆续跑来连接,分别执行 P 操作,S 变成 2、1、0。它们都顺利拿到了数据库连接。
  3. 突然线程 4 也想连,执行 P 操作,S 变成 -1。对不起,线程 4 只能在连接池门口排队苦等
  4. 直到线程 2 用完数据库断开连接了,执行 V 操作,S 变成 0,排队的线程 4 瞬间被唤醒,顶替进去。

🏁 总结

在实际的 Java 开发中,你可能不需要天天去调用 Linux 底层的 C 语言信号量,因为 Java 在语言层面早就把这套机制封装成了高级工具类:

  • Java 里的 ReentrantLock(可重入锁) → \rightarrow 本质就是场景一的 互斥信号量
  • Java 里的 Semaphore → \rightarrow 本质就是场景三的 计数信号量(在需要做多线程接口限流、控制并发连接数时天天用)。

所以,信号量不是什么虚无缥缈的概念,它就是多线程/多进程世界里的法官和指挥官,没有它,高并发的代码就会瞬间乱成一锅粥!

Logo

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

更多推荐