操作系统经典算法 Java 实现:进程同步、死锁与内存管理
操作系统经典算法 Java 实现:进程同步、死锁与内存管理
一、 实验背景
《操作系统》是计算机科学的基石。书本上的进程调度、并发控制和虚拟内存等概念通常较为抽象。为了深入理解操作系统是如何在底层协调硬件资源并管理多任务运行的,在课程实验环节,我使用 Java 语言将这些核心调度算法进行了代码级的模拟与实现。
本项目的代码(主要集中在 fendou 目录)涵盖了操作系统三大核心议题:进程同步与互斥、死锁避免以及页面置换策略。
二、 进程同步:生产者-消费者模型
在多任务环境中,当多个进程(或线程)并发访问共享资源时,极易产生数据不一致的问题(即竞态条件)。“生产者-消费者问题”是解决此类并发问题的经典模型。
2.1 核心代码逻辑
在实验代码中,我利用 Java 的原生多线程机制模拟了这一过程。
- 临界资源:定义了一个具有固定容量的数组(或队列)作为“缓冲区”。
- 互斥锁:使用
synchronized关键字对缓冲区对象进行加锁,确保同一时刻只有一个线程能够执行生产或消费操作。 - 同步通信:当缓冲区满时,生产者线程调用
wait()方法释放锁并进入阻塞状态;当消费者取走数据后,调用notifyAll()唤醒生产者。同理,当缓冲区为空时,消费者等待生产者生产数据。这种基于等待-唤醒机制的设计,完美实现了进程间的同步。
三、 死锁避免:银行家算法的推演
死锁是指多个进程互相等待对方释放资源,导致系统陷入永久停滞的现象。为了避免死锁,操作系统在分配资源时必须进行前瞻性的安全性检查。
3.1 银行家算法 (Banker’s Algorithm) 实现
我使用 Java 完整编写了由 Dijkstra 提出的银行家算法(Bank.java)。
- 数据结构:代码中维护了几个核心的二维矩阵:
Available:系统当前可用的各类资源数量。Max:各进程对各类资源的最大需求量。Allocation:已分配给各进程的资源量。Need:各进程还需要的资源量(等于 Max - Allocation)。
- 安全性检查:当某个进程提出资源申请时,系统不会立刻分配,而是进行一次试探性分配。随后,程序运行“安全性检查算法”,尝试在剩余资源中寻找一个能够让所有进程顺利执行完毕的“安全序列”。如果存在这样的序列,则正式分配资源;否则,拒绝申请并让该进程等待。
通过编写这段代码,我深刻理解了系统状态的转换逻辑以及严谨的矩阵运算在系统安全中的应用。
四、 虚拟内存管理:页面置换算法对比
当程序运行时,物理内存可能无法容纳所有的数据页。操作系统需要一种策略,将暂时不用的页面换出到磁盘,并将需要的页面调入内存。
4.1 算法模拟与性能对比
实验中,我模拟了一个包含固定数量物理块(如 3 块或 4 块)的内存系统,并生成了一串随机的页面访问序列。
- FIFO (先进先出) 算法:利用队列结构实现。每次发生缺页中断时,淘汰掉最早进入内存的页面。这种算法实现简单,但在某些特定序列下会出现“Belady 异常”(分配的物理块增加,缺页率反而上升)。
- LRU (最近最少使用) 算法:利用链表或基于时间戳的数组实现。每次淘汰掉最长时间未被访问的页面。在代码中,每次页面被访问时,我都会更新其在数据结构中的位置或时间戳。
通过运行对比测试,程序输出了两种算法的“缺页中断率”。实验数据直观地证明了,基于局部性原理的 LRU 算法在大多数情况下的性能显著优于 FIFO。
五、 实验总结与工程启发
将高深莫测的操作系统理论转化为可执行的 Java 代码,极大地降低了这些概念的理解门槛。
更重要的是,这些底层算法的思想在现代软件工程中随处可见。例如:
- 生产者-消费者模型是现代消息队列(如 Kafka, RabbitMQ)的理论基础。
- LRU 算法被广泛应用于各种缓存系统(如 Redis 的数据淘汰策略、浏览器缓存)中。
通过这次实验,我不仅夯实了计算机基础理论,更为后续学习高并发后端开发和系统架构设计提供了重要的理论支撑。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 小枝的博客!