一生產(chǎn)者多消費者 --- 操作棧問題

一生產(chǎn)者多消費者 — 操作棧問題
本文是想通過棧的方式來進(jìn)行線程間通訊。

  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狀態(tài)");
         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();
      }
   }

}

主函數(shù)如下:

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

}

運行如上代碼,會出現(xiàn)如下異常:

push=1
pop操作中的:Thread-2 線程呈wait狀態(tài)
pop=0
push=1
pop操作中的:Thread-2 線程呈wait狀態(tài)
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類中,對戰(zhàn)的棧的操作時用了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狀態(tài)");
         list.remove(0);
         this.notify();
         System.out.println("pop=" + list.size());
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
      return returnValue;
   }

我們知道this.norify()操作會隨機(jī)喚醒一個在當(dāng)前鎖對象上正在等待的線程,假如當(dāng)前l(fā)ist.size==0為true,這時候有一個consumer線程consumer1調(diào)用了this.wait(),此時consumer1線程釋放鎖,當(dāng)前線程的執(zhí)行流程暫停在了this.wait()這里。 produce線程做了一次push操作,然后喚醒了consumer2線程,consumer2執(zhí)行了remove操作之后,調(diào)用了this.norify(),此時把consumer1線程喚醒,consumer1接著上次的位置繼續(xù)執(zhí)行,執(zhí)行l(wèi)ist.remove()操作,由于此時list里面為空,所以拋IndexOutOfBoundsException異常。

怎么解決這個問題呢?想要解決這個問題,只需要把if判斷,改成while循環(huán)即可。

package entity;

import java.util.ArrayList;
import java.util.List;

public class MyStack {
   private List list = new ArrayList();

   synchronized public void push() {
      try {
          /**修改這里為while循環(huán)*/
         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循環(huán)*/
         while (list.size() == 0) {
            this.wait();
         }
         returnValue = "" + list.get(0);
         System.out.println("pop操作中的:"
               + Thread.currentThread().getName() + " 線程呈wait狀態(tài)");
         list.remove(0);
         this.notify();
         System.out.println("pop=" + list.size());
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
      return returnValue;
   }
}

但是這種情況下。異常問題解決了,又會迎來假死的情況,下面來探討假死情況

2.假死情況
假死產(chǎn)生的原因就是所以的線程都執(zhí)行了this.wait(),這種情況也很好理解。例如,程序運行,consumer1線程剛調(diào)用了this.wait()方法,produce線程往棧里添加了一個元素,這時候produce執(zhí)行notify操作,喚醒了consumer2,consumer2執(zhí)行了remove之后,調(diào)用notify喚醒的是consumer1,而consumer1接著this.wait()的代碼繼續(xù)執(zhí)行,又進(jìn)入了while循環(huán),再次調(diào)用了this.wait(),而consumer2也由于外層while循環(huán),重新調(diào)用pop操作,也進(jìn)入了wait狀態(tài)。此時所有的線程都在wait,整個程序進(jìn)入了假死。

解決這個問題的方法就是把this.notify()改成this.nofify()。 程序運行便不會在出現(xiàn)假死,并且可以正常運行下去:

package entity;

import java.util.ArrayList;
import java.util.List;

public class MyStack {
   private List list = new ArrayList();

   synchronized public void push() {
      try {
          /**修改這里為while循環(huán)*/
         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循環(huán)*/
         while (list.size() == 0) {
            this.wait();
         }
         returnValue = "" + list.get(0);
         System.out.println("pop操作中的:"
               + Thread.currentThread().getName() + " 線程呈wait狀態(tài)");
         list.remove(0);
         this.notifyAll();
         System.out.println("pop=" + list.size());
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
      return returnValue;
   }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容