我用线程模拟生产者消费者操作栈存取数据,当只有一对生产者消费者线程时,代码不报错,结果线程数加倍后就报了错,话不多说先上代码。
//生产者线程
public class Producer extends Thread {
private Stack stack;
public Producer(Stack stack) {
super();
this.stack = stack;
}
public void run() {
while (true) {
// 随机生成a~z的英文字符,97 +[0,26)
char c = (char) ('a' + new Random().nextInt(26));
synchronized (stack) {
if (stack.isFull()) {
try {
stack.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//往栈中压入字符
stack.push(c);
System.out.println("压入字符" + c);
stack.notifyAll();
}
}
}
}
//消费者线程
public class Consumer extends Thread {
private Stack stack;
public Consumer(Stack stack) {
super();
this.stack = stack;
}
public void run() {
while (true) {
synchronized (stack) {
if (stack.isEmpty()) {
try {
// 在栈对象上等待 收到通知后醒来继续执行
stack.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//取出栈中字符
char c = stack.pop();
System.out.println("取出字符" + c);
stack.notifyAll();
}
}
}
}
//被操作的对象栈
public class Stack {
private char[] cr = new char[5];
private int index = 0;
public void push(char c) {
cr[index] = c;
index++;
}
public char pop() {
// System.out.println("下标的值 " + index);
index--;
char c = cr[index];
return c;
}
public boolean isEmpty() {
// System.out.println("数组为空判断 " + (index == 0));
return index == 0;
}
public boolean isFull() {
return index == 5;
}
}
//测试类
public class TestStack {
/**
* @Title: main
* @Description: 描述这个方法的作用
* @param: @param args 参数说明
* @return: void 返回类型
* @throws
*/
public static void main(String[] args) {
Stack stack = new Stack();
for (int i = 0; i < 1; i++) {
Producer producer = new Producer(stack);
producer.start();
Consumer consumer = new Consumer(stack);
consumer.start();
}
}
}
启动测试类,程序正常运行不报错,但是当线程数加大时,同步代码块报错,出现异常。
异常信息:Exception in thread "Thread-3" Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: Index -1 out of bounds for length 5 at com.dapang.test.Stack.pop(Stack.java:31)
at com.dapang.test.Consumer.run(Consumer.java:39)
java.lang.ArrayIndexOutOfBoundsException: Index -1 out of bounds for length 5
at com.dapang.test.Stack.push(Stack.java:24)
at com.dapang.test.Producer.run(Producer.java:42)
后来经过请教才发现,问题是我对同步代码块的理解有误。当线程执行wait方法,后又被唤醒进入执行状态,重新拿到锁进入同步代码块时,不是重新执行同步代码块,而是从原来阻塞的地方继续执行,所以在线程对栈执行操作之前需要再判断一次,才能保证不再出现异常。
在同步代码块中添加的判断代码,我粘出来,仅供参考。
生产者中:
if (stack.isFull()) {
continue;
}
消费者中:
if (stack.isEmpty()) {
continue;
}