经典的操作系统实验题目:生产者消费者实验
本科的时候写的C语言版本,只是通过信号量机制来保证互斥的访问,这次用Java来写完全是一个全新的理解。
一个生产者一个消费者:
情形1:生产者负责生产商品,生产完一件商品就会停止生产,消费者负责消费产品,消费掉一件商品之后就会停止消费,等待生产。
main方法:
String lock = new String(); P p = new P(lock); C c = new C(lock); ThreadP pThread = new ThreadP(p); ThreadC cThread = new ThreadC(c); pThread.start(); cThread.start();
运行结果:
仓库为空,生产者开始生产—代号:1575509334730_28388903225180
生产结束了!
有商品了,可以进行消费了! 1575509334730_28388903225180
消费结束了!
仓库为空,生产者开始生产—代号:1575509334730_28388903262202
生产结束了!
有商品了,可以进行消费了! 1575509334730_28388903262202
消费结束了!
......
可以看出他们是交替运行的,如果是多生产者和多消费者情况下可能会出现都在等待的状态,出现“假死”现象。
情形2:生产者可以一直生产商品,仓库的容量为n,生产的时候不可以进行消费。即:
- 在缓冲区为空时,消费者不能再进行消费
- 在缓冲区为满时,生产者不能再进行生产
- 在一个线程进行生产或消费时,其余线程不能再进行生产或消费等操作,即保持线程间的同步
- 注意条件变量与互斥锁的顺序
在情形2的代码实现之前先看一个案例,当我们试图使用情形一的思路拓展到情形二时会出现下面的案例。
生产者消费者多线程假死的案例:
假死的原因是notify连续唤醒了同类,多消费者多生产者情况下,消费者消费后连续得唤醒了消费者,生产者生产后连续得唤醒了生产者,就会出现进程假死的情况,这种情况下需要使用notifyall方法。因为notify方法是随机唤醒一个线程的,所以会消费者唤醒消费者,消费者唤醒生产者,生产者唤醒消费者,生产者唤醒生产者,当「生产者唤醒生产者」与「消费者唤醒消费者」的情形积少成多时,就会出现假死的案例。所以通过使用notifyall来唤醒全部的线程,避免来出现上述的情况。
public class MyStack { private List list = new ArrayList(); synchronized public void push() { try { while (list.size() == 1) { this.wait(); } list.add("anyString=" + Math.random()); this.notifyAll(); System.out.println("push=" + list.size()); } catch (InterruptedException e) { e.printStackTrace(); } } synchronized public String pop() { String returnValue = ""; try { while (list.size() == 0) { System.out.println("pop操作中的:" + Thread.currentThread().getName() + " 线程呈wait状态"); this.wait(); } returnValue = "" + list.get(0); list.remove(0); this.notifyAll(); System.out.println("pop=" + list.size()); } catch (InterruptedException e) { e.printStackTrace(); } return returnValue; } } public class P { private MyStack myStack; public P(MyStack myStack) { super(); this.myStack = myStack; } public void pushService() { myStack.push(); } } public class C { private MyStack myStack; public C(MyStack myStack) { super(); this.myStack = myStack; } public void popService() { System.out.println("pop=" + myStack.pop()); } } public class P_Thread extends Thread { private P p; public P_Thread(P p) { super(); this.p = p; } @Override public void run() { while (true) { p.pushService(); } } } public class C_Thread extends Thread { private C r; public C_Thread(C r) { super(); this.r = r; } @Override public void run() { while (true) { r.popService(); } } } public class Run { public static void main(String[] args) { MyStack myStack = new MyStack(); P p1 = new P(myStack); P p2 = new P(myStack); P p3 = new P(myStack); P p4 = new P(myStack); P p5 = new P(myStack); P p6 = new P(myStack); P_Thread pThread1 = new P_Thread(p1); P_Thread pThread2 = new P_Thread(p2); P_Thread pThread3 = new P_Thread(p3); P_Thread pThread4 = new P_Thread(p4); P_Thread pThread5 = new P_Thread(p5); P_Thread pThread6 = new P_Thread(p6); pThread1.start(); pThread2.start(); pThread3.start(); pThread4.start(); pThread5.start(); pThread6.start(); C r1 = new C(myStack); C r2 = new C(myStack); C r3 = new C(myStack); C r4 = new C(myStack); C r5 = new C(myStack); C r6 = new C(myStack); C r7 = new C(myStack); C r8 = new C(myStack); C_Thread cThread1 = new C_Thread(r1); C_Thread cThread2 = new C_Thread(r2); C_Thread cThread3 = new C_Thread(r3); C_Thread cThread4 = new C_Thread(r4); C_Thread cThread5 = new C_Thread(r5); C_Thread cThread6 = new C_Thread(r6); C_Thread cThread7 = new C_Thread(r7); C_Thread cThread8 = new C_Thread(r8); cThread1.start(); cThread2.start(); cThread3.start(); cThread4.start(); cThread5.start(); cThread6.start(); cThread7.start(); cThread8.start(); } }