一生产者多消费者 --- 操作栈问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lchpersonal521/article/details/84582619

一生产者多消费者 — 操作栈问题

本文是想通过栈的方式来进行线程间通讯。

1. 异常情况

看如下代码:

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;
   }
}

猜你喜欢

转载自blog.csdn.net/lchpersonal521/article/details/84582619