Java多執行緒——消費者與生產者的關係

ckxllf發表於2019-12-26

  多執行緒:CPU中各種任務在交替執行過程中,被稱為多執行緒處理。其中,每個任務的一次動態執行過程被稱為程式。程式執行過程中的暫停被稱為中斷。程式透過中斷被分解成若干段,每一段被稱為一個執行緒。

  在消費者與生產者中,我們設定單個生產者的每次活動是饅頭 +1;單個消費者每次進行的是饅頭 -1;然後消費者和生產者的活動時間不確定,同時生產者和消費者的數量不確定,該經典問題我們使用多執行緒實現:

  我們首先確定實物,即生產者的每次生成的物品,在這裡我們使用饅頭代替。在WoTou的類中,我們可以看到有著自己獨特的屬性,即饅頭的 id,我們可以在控制檯清晰的看到饅頭的生產和消費過程,具體實現程式碼如下:

  class WoTou{ //饅頭,生產者生產的饅頭

  int id;

  WoTou(int id){

  this.id = id;

  }

  public String toString(){

  return "WoTou:"+ id;

  }

  }

  我們需要確定饅頭的儲存容器,即在消費和生產發生的時候,我們需要在什麼地方進行判斷,饅頭是否耗盡,或者饅頭是否滿溢。每次活動都是相當於在棧中取東西和放東西,所以我們根據棧的特性為:先進後出 原則。

  class SyncStack {//籃子容器,放饅頭用的

  int index = 0;

  WoTou[] arrWT = new WoTou[6];

  }

  在籃子容器中我們簡單定義棧的最大容量為6,我們之後設定生產者的類,定義為producer類。我們知道producer有著一些自己的獨特屬性,即往籃子SyncStack中放WoTou,每次進行饅頭+1操作,還有生產者的操作Time間隔,是隔10毫秒還是5毫秒,我們使得producer類實現其Runnerable介面,呼叫執行緒的run()方法,具體程式碼如下:

  class Producer implements Runnable{ //生產者

  SyncStack ss= null;

  Producer(SyncStack ss){

  this.ss = ss;

  }

  public void run(){

  for(int i=0;i<20;i++){

  WoTou wt = new WoTou(i);

  ss.push(wt);

  System.out.println("生產者:"+wt);

  try {

  Thread.sleep((int)(Math.random()*1000));

  } catch (InterruptedException e) {

  // TODO: handle exception

  e.printStackTrace();

  }

  }

  }

  }

  生產者在往棧中新增資料的時候,我們使用push方法進行插入。同時定義插入的饅頭數量,這裡的數量並不是籃子的饅頭數量。籃子裡邊雖然只能一次性裝6個饅頭,但是消費者是同時間進行操作的,所以籃子的饅頭數量在消費之後,生產者繼續進行生產,一直到生產20個饅頭為止。

  消費者我們這裡定義為Consumer類,消費者和生產者有一定的相似性,但是我們需要消費在籃子中拿的過程,我們使用pop操作,同時定義消費數量,以及操作間隔,這裡都是使用執行緒的sleep(時間)方法, 程式碼如下:

  class Consumer implements Runnable{ //消費者

  SyncStack ss= null;

  Consumer(SyncStack ss){

  this.ss = ss;

  }

  public void run(){

  for(int i=0;i<20;i++){

  WoTou wt = ss.pop();

  System.out.println("消費量:"+wt);

  try {

  Thread.sleep((int)(Math.random()*1000));

  } catch (InterruptedException e) {

  // TODO: handle exception

  e.printStackTrace();

  }

  }

  }

  }

  最後我們考慮在籃子中,假如容量已滿,即生產者生產過多,消費的太慢,那麼生產者需要休息;同樣的消費過快,容器數量為0,消費者開始休息。我們在兩種情況發生的時候,需要設定兩種方法,在極限情況發生的時候,進行判斷。簡單實現程式碼如下:

  public synchronized void push(WoTou wt){ //往籃子中放饅頭

  while(index == arrWT.length){

  try {

  this.wait();

  } catch (InterruptedException e) {

  // TODO: handle exception

  e.printStackTrace();

  }

  }

  this.notify();

  arrWT[index] = wt;

  index++;

  } 鄭州人流多少錢

  我們需要確定是我們在生產者進行活動的時候,不能受到其他執行緒的影響,即我們需要保持在某一時刻只有一個執行緒進行操作,這種情況我們需要使用執行緒的一個synchronized方法。這個方法稱為死鎖,鎖定當前方法體執行的過程中,保持其獨立性。即在方法體的前面使用synchronized關鍵字修飾。

  從籃子中取出饅頭的操作類似於生產者,實現程式碼如下:

  public synchronized WoTou pop(){ //從籃子中取饅頭

  while(index == 0){

  try {

  this.wait();

  } catch (InterruptedException e) {

  // TODO: handle exception

  e.printStackTrace();

  }

  }

  this.notify();

  index--;

  return arrWT[index];

  }

  我們會發現在上面兩種動作中,考慮到一個問題,即數量到極限之後,我們的動作停止了。生產者在籃子容量到達6時,動作停止;消費者在籃子數量到0後,停止行為。我們這時候開始使用執行緒的notify方法,喚醒休眠的執行緒,因為執行緒使用了wait方法,我們可以喚醒之後,在進行各自的行為。因此籃子SynStack類中詳細程式碼為:

  class SyncStack {//籃子容器,放饅頭用的

  int index = 0;

  WoTou[] arrWT = new WoTou[6];

  public synchronized void push(WoTou wt){ //往籃子中放饅頭

  while(index == arrWT.length){

  try {

  this.wait(); //執行緒等待

  } catch (InterruptedException e) {

  // TODO: handle exception

  e.printStackTrace();

  }

  }

  this.notify(); //喚醒wait的執行緒

  arrWT[index] = wt;

  index++;

  }

  public synchronized WoTou pop(){ //從籃子中取饅頭

  while(index == 0){

  try {

  this.wait();

  } catch (InterruptedException e) {

  // TODO: handle exception

  e.printStackTrace();

  }

  }

  this.notify();

  index--;

  return arrWT[index];

  }

  }

  我們可以修改生產者和消費者的數量和操作間隔,不一定生產+1同時消費-1。我們可以根據修改進行更改程式碼,上面的執行在控制檯資料


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69945560/viewspace-2670680/,如需轉載,請註明出處,否則將追究法律責任。

相關文章