在多道程序系统中,同时有多个进程并发运行,共享系统资源,从而提高了系统资源利用率,提高了系统的处理能力。但是,若对资源的管理、分配和使用不当,则会产生死锁或是饥饿。
所谓死锁是指在多道程序系统中,一组进程中的每一个进程都无限期等待被该组进程中的另一个进程所占有且永远不会释放的资源。
饥饿是指系统不能保证某个进程的等待时间上界,从而使该进程长时间等待,当等待时间给进程推进和响应带来明显影响时,称发生了进程饥饿。当饥饿到一定程度的进程所赋予的任务即使完成也不再具有实际意义时称该进程被饿死。
下面我们就来分别讨论一下死锁与饥饿各自的特点。
死锁
产生死锁的原因主要有两个,一是竞争资源,系统提供的资源数量有限,不能满足每个进程的需求;二是多道程序运行时,进程推进顺序不合理。由此可见,发生死锁时死锁进程的个数至少是两个。我们可以举一个最简单的例子来了解一下死锁:
假如双方都拥有部分资源(P1拥有A,P2拥有B,且A,B均只有一个),但这时P1还需要B,P2还需要A,于是P1与P2都会处在无限等待状态,发生了死锁。
由这个例子,我们可以归纳分析出产生死锁的必要条件:
(1) 互斥条件
资源是独占的且排他使用。即任意时刻一个资源只能给一个进程使用,其他申请者只有等待,直到资源被占有者释放。如例子中的A,B资源。
(2) 不可剥夺条件
进程所获得的资源在未使用完毕之前,不能被其他进程强行剥夺,而只能由拥有该资源的进程自愿释放。如例子中P2 不能强占P1拥有的A资源,而P1也不能强占P2拥有的B资源。
(3) 请求和保持条件
进程每次申请他所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源。如例子中P1申请B资源时继续占有A资源,P2申请A资源时继续占有B资源。
(4) 循环等待条件
在发生死锁时,必然存在一个进程等待环路,环路中的每一个进程已占有的资源同时被另一个进程所申请。如例子中的P1和P2就是一个简单的等待环路。
系统发生死锁不仅浪费了大量的系统资源,甚至会导致整个系统的崩溃,因此如何解决死锁问题是操作系统设计中的一个重点。目前主要有两类方法,一类是不让死锁发生;另一类是让死锁发生,再加以解决。
死锁预防就是力图不让死锁发生,它采用破坏“不可剥夺”条件,或破坏“请求保持”条件,或破坏“循环等待”条件来达到目的,但这种方法给系统加上了较强的限制条件,严重的影响了系统性能。死锁避免则是不破坏死锁的必要条件,而是系统对进程发出的每一个系统能够满足的资源申请进行动态检验,并根据检验结果决定是否分配资源,如果分配后系统可能发生死锁,则不分配,否则分配。这种方法算法复杂,会消耗很多的系统时间。
死锁的检测与解除则属于让死锁发生,再加以解决的方法。操作系统不断的监督进程的进展路径,一旦检测到死锁的发生,则采用专门的措施解除死锁,并以最小的代价使整个系统恢复正常。
饥饿
产生饥饿的主要原因是:在一个动态系统中,对于每类系统资源,操作系统需要确定一个分配策略,当多个进程同时申请某类资源时,由分配策略确定资源分配给进程的次序。有时资源分配策略可能是不公平的,即不能保证等待时间上界的存在
。在这种情况下,即使系统没有发生死锁,某些进程也可能会长时间等待.当等待时间给进程推进和响应带来明显影响时,称发生了进程饥饿,当饥饿到一定程度的进程所赋予的任务即使完成也不再具有实际意义时称该进程被饿死。举个例子,当有多个进程需要打印文件时,如果系统分配打印机的策略是最短文件优先,那么长文件的打印任务将由于短文件的源源不断到来而被无限期推迟,导致最终的饥饿甚至饿死。
饥饿没有其产生的必要条件,随机性很强。并且饥饿可以被消除,因此也将忙等待时发生的饥饿称为活锁。
由于饥饿和饿死与资源分配策略有关,因而解决饥饿与饿死问题可从资源分配策略的公平性考虑,确保所有进程不被忽视。如时间片轮转算法(RR)。它将CPU的处理时间分成一个个时间片,就绪队列中的诸进程轮流运行一个时间片,当时间片结束时,就强迫运行程序让出CPU,该进程进入就绪队列,等待下一次调度。同时,进程调度又去选择就绪队列中的一个进程,分配给它一个时间片,以投入运行。如此方式轮流调度。这样就可以在不考虑其他系统开销的情况下解决饥饿的问题。
死锁和饥饿的异同
相同点:二者都是由于竞争资源而引起的。
不同点:
- 从进程状态考虑,死锁进程都处于等待状态,忙等待(处于运行或就绪状态)的进程并非处于等待状态,但却可能被饿死;
- 死锁进程等待永远不会被释放的资源,饿死进程等待会被释放但却不会分配给自己的资源,表现为等待时限没有上界(排队等待或忙式等待);
- 死锁一定发生了循环等待,而饿死则不然。这也表明通过资源分配图可以检测死锁存在与否,但却不能检测是否有进程饿死;
- 死锁一定涉及多个进程,而饥饿或被饿死的进程可能只有一个。
- 在饥饿的情形下,系统中有至少一个进程能正常运行,只是饥饿进程得不到执行机会。而死锁则可能会最终使整个系统陷入死锁并崩溃。
分享到:
相关推荐
- **活锁与饥饿**:对比死锁,理解活锁和饥饿现象。 - **Double-Check Locking**:理解双重检查锁定模式,以及其在单例模式中的应用。 - ** volatile与synchronized的区别**:深入探讨两者的异同。 通过本课程的...
- 死锁、活锁和饥饿现象。 7. **IO流**: - 字节流和字符流:了解它们的区别和使用场景。 - 文件操作:读写文件,包括BufferedReader、BufferedWriter等类的使用。 - 转换流:InputStreamReader和...
实例中可能涵盖了这两种方式的使用,并通过不同示例解释它们的异同和适用场景。 二、线程控制 1. 同步机制:包括synchronized关键字和Lock接口(如ReentrantLock可重入锁)。synchronized用于解决共享资源的并发...
9. **多线程**:线程的创建与管理,同步控制(如synchronized、Lock),以及死锁、活锁和饥饿现象的理解。 10. **JVM优化**:包括堆内存分配、栈内存管理、方法区、类加载机制等,了解JVM调优技巧对于提高程序性能...
- 同步机制:理解synchronized关键字的用法,以及死锁、活锁和饥饿的概念。 - Lock接口:学习ReentrantLock、ReadWriteLock等,了解它们相比synchronized的优势。 4. **IO与NIO** - 流的概念:理解字节流和字符...
8. **多线程**:线程的创建方式,线程同步机制(synchronized、Lock),死锁、活锁、饥饿的概念。 9. **并发编程**:ExecutorService、Future、Callable接口的理解,以及并发工具类如Semaphore、CountDownLatch、...
- **同步机制**:理解synchronized关键字,死锁、活锁和饥饿现象。 - **并发工具类**:了解Semaphore、CyclicBarrier、CountDownLatch等并发工具的使用。 7. **设计模式** - **单例模式**:熟悉各种单例实现方式...
- **同步机制**:理解synchronized关键字的用法,以及死锁、活锁、饥饿等问题。 - **并发工具类**:如Semaphore、CountDownLatch、CyclicBarrier、ThreadPoolExecutor等的使用。 7. **IO与NIO** - **流的分类**...
3. **同步与互斥**:synchronized关键字、Lock接口(如ReentrantLock)的使用,以及死锁、活锁和饥饿现象。 4. **线程间通信**:wait()、notify()和notifyAll()方法的使用,以及BlockingQueue的线程安全特性。 5. ...
2. 线程安全:理解线程安全问题,如死锁、活锁、饥饿等,以及解决策略。 3. 并发工具:熟悉并发容器如ConcurrentHashMap、BlockingQueue,以及CountDownLatch、CyclicBarrier、Semaphore等并发工具。 四、Java IO/...
要掌握线程的创建、同步机制(如synchronized、volatile、Lock接口),了解线程池的工作原理,以及死锁、活锁、饥饿状态等问题的防范。 4. **设计模式**:设计模式是软件设计的经典思想,包括单例模式、工厂模式、...
- **同步机制**:synchronized关键字、volatile关键字、Lock接口及其实现类,以及死锁、活锁、饥饿状态的理解。 - **线程池**:ExecutorService、ThreadPoolExecutor和Executors的使用。 5. **异常处理**: - **...
- **同步机制**:synchronized关键字的使用,死锁、活锁、饥饿现象。 - **线程池**:ExecutorService的理解,线程池的创建和管理。 5. **IO流** - **流的分类**:字节流与字符流,输入流与输出流。 - **缓冲流*...