一生产者多消费者 — 操作栈问题
本文是想通过栈的方式来进行线程间通讯。
- 异常情况
看如下代码:
package entity;
import java.util.ArrayList;
import java.util.List;
public class MyStack {
private List list = new ArrayList();
synchronized public void push() {
try {
if (list.size() == 1) {
this.wait();
}
list.add("anyString=" + Math.random());
this.notify();
System.out.println("push=" + list.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public String pop() {
String returnValue = "";
try {
if (list.size() == 0) {
this.wait();
}
returnValue = "" + list.get(0);
System.out.println("pop操作中的:"
+ Thread.currentThread().getName() + " 线程呈wait状态");
list.remove(0);
this.notify();
System.out.println("pop=" + list.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
return returnValue;
}
}
package extthread;
import entity.MyStack;
public class Consumer_Thread extends Thread {
private MyStack myStack;
public Consumer_Thread(MyStack r) {
this.myStack = r;
}
@Override
public void run() {
while (true) {
myStack.pop();
}
}
}
package extthread;
import entity.MyStack;
public class Produce_Thread extends Thread {
private MyStack myStack;
public Produce_Thread(MyStack p) {
this.myStack = p;
}
@Override
public void run() {
while (true) {
myStack.push();
}
}
}
主函数如下:
package test.run;
import entity.MyStack;
import extthread.Consumer_Thread;
import extthread.Produce_Thread;
public class Run {
public static void main(String[] args) throws Exception{
MyStack myStack = new MyStack();
Produce_Thread pThread = new Produce_Thread(myStack);
pThread.start();
Consumer_Thread cThread1 = new Consumer_Thread(myStack);
Consumer_Thread cThread2 = new Consumer_Thread(myStack);
cThread1.start();
cThread2.start();
}
}
运行如上代码,会出现如下异常:
push=1
pop操作中的:Thread-2 线程呈wait状态
pop=0
push=1
pop操作中的:Thread-2 线程呈wait状态
pop=0
Exception in thread "Thread-1" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.rangeCheck(ArrayList.java:653)
at java.util.ArrayList.get(ArrayList.java:429)
at entity.MyStack.pop(MyStack.java:28)
at extthread.Consumer_Thread.run(Consumer_Thread.java:16)
究其原因,是由于在Mystack类中,对战的栈的操作时用了if来判断:
synchronized public String pop() {
String returnValue = "";
try {
if (list.size() == 0) {
this.wait();
}
returnValue = "" + list.get(0);
System.out.println("pop操作中的:"
+ Thread.currentThread().getName() + " 线程呈wait状态");
list.remove(0);
this.notify();
System.out.println("pop=" + list.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
return returnValue;
}
我们知道this.norify()操作会随机唤醒一个在当前锁对象上正在等待的线程,假如当前list.size==0为true,这时候有一个consumer线程consumer1调用了this.wait(),此时consumer1线程释放锁,当前线程的执行流程暂停在了this.wait()这里。 produce线程做了一次push操作,然后唤醒了consumer2线程,consumer2执行了remove操作之后,调用了this.norify(),此时把consumer1线程唤醒,consumer1接着上次的位置继续执行,执行list.remove()操作,由于此时list里面为空,所以抛IndexOutOfBoundsException异常。
怎么解决这个问题呢?想要解决这个问题,只需要把if判断,改成while循环即可。
package entity;
import java.util.ArrayList;
import java.util.List;
public class MyStack {
private List list = new ArrayList();
synchronized public void push() {
try {
/**修改这里为while循环*/
while (list.size() == 1) {
this.wait();
}
list.add("anyString=" + Math.random());
this.notify();
System.out.println("push=" + list.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public String pop() {
String returnValue = "";
try {
/**修改这里为while循环*/
while (list.size() == 0) {
this.wait();
}
returnValue = "" + list.get(0);
System.out.println("pop操作中的:"
+ Thread.currentThread().getName() + " 线程呈wait状态");
list.remove(0);
this.notify();
System.out.println("pop=" + list.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
return returnValue;
}
}
但是这种情况下。异常问题解决了,又会迎来假死的情况,下面来探讨假死情况
2.假死情况
假死产生的原因就是所以的线程都执行了this.wait(),这种情况也很好理解。例如,程序运行,consumer1线程刚调用了this.wait()方法,produce线程往栈里添加了一个元素,这时候produce执行notify操作,唤醒了consumer2,consumer2执行了remove之后,调用notify唤醒的是consumer1,而consumer1接着this.wait()的代码继续执行,又进入了while循环,再次调用了this.wait(),而consumer2也由于外层while循环,重新调用pop操作,也进入了wait状态。此时所有的线程都在wait,整个程序进入了假死。
解决这个问题的方法就是把this.notify()改成this.nofify()。 程序运行便不会在出现假死,并且可以正常运行下去:
package entity;
import java.util.ArrayList;
import java.util.List;
public class MyStack {
private List list = new ArrayList();
synchronized public void push() {
try {
/**修改这里为while循环*/
while (list.size() == 1) {
this.wait();
}
list.add("anyString=" + Math.random());
this.notify();
System.out.println("push=" + list.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public String pop() {
String returnValue = "";
try {
/**修改这里为while循环*/
while (list.size() == 0) {
this.wait();
}
returnValue = "" + list.get(0);
System.out.println("pop操作中的:"
+ Thread.currentThread().getName() + " 线程呈wait状态");
list.remove(0);
this.notifyAll();
System.out.println("pop=" + list.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
return returnValue;
}
}