首頁 >web前端 >js教程 >java多執行緒之並發協作生產者消費者設計模式

java多執行緒之並發協作生產者消費者設計模式

伊谢尔伦
伊谢尔伦原創
2016-11-21 13:31:371859瀏覽

 兩個線程一個生產者個一個消費者

  需求情景

  兩個線程,一個負責生產,一個負責消費,生產者生產一個,消費者消費一個

『 資源被多個執行緒並發存取時的完整性。常用的同步方法是採用標記或加鎖機制

  wait() / nofity() 方法是基類Object的兩個方法,也就意味著所有Java類別都會擁有這兩個方法,這樣,我們就可以為任何物件實作同步機制。

  wait()方法:當緩衝區已滿/空時,生產者/消費者執行緒停止自己的執行,放棄鎖,使自己處於等等狀態,讓其他執行緒執行。

  notify()方法:當生產者/消費者向緩衝區放入/取出一個產品時,向其他等待的執行緒發出可執行的通知,同時放棄鎖,使自己處於等待狀態。

  程式碼實作(共三個類別和一個main方法的測試類別)


  Resource.java
  /**
  * Created by yuandl on 2016-10-11./**
  * 资源
  */
  public class Resource {
  /*资源序号*/
  private int number = 0;
  /*资源标记*/
  private boolean flag = false;
  /**
  * 生产资源
  */
  public synchronized void create() {
  if (flag) {//先判断标记是否已经生产了,如果已经生产,等待消费;
  try {
  wait();//让生产线程等待
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  }
  number++;//生产一个
  System.out.println(Thread.currentThread().getName() + "生产者------------" + number);
  flag = true;//将资源标记为已经生产
  notify();//唤醒在等待操作资源的线程(队列)
  }
  /**
  * 消费资源
  */
  public synchronized void destroy() {
  if (!flag) {
  try {
  wait();
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  }
  System.out.println(Thread.currentThread().getName() + "消费者****" + number);
  flag = false;
  notify();
  }
 }

  Producer.java

  ProducerConsumerTest.java

  /**
  * Created by yuandl on 2016-10-11.
  *
  /**
  * 生产者
  */
  public class Producer implements Runnable {
  private Resource resource;
  public Producer(Resource resource) {
  this.resource = resource;
  }
  @Override
  public void run() {
  while (true) {
  try {
  Thread.sleep(10);
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  resource.create();
  }
  }
  }

打印結果

 /**
  * 消费者
  */
  public class Consumer implements Runnable {
  private Resource resource;
  public Consumer(Resource resource) {
  this.resource = resource;
  }
  @Override
  public void run() {
  while (true) {
  try {
  Thread.sleep(10);
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  resource.destroy();
  }
  }
  }

以上印刷結果可以看出沒有任何問題

  多個線程,多個生產者和多個消費者的問題

  多個需求

『兩個個負責消費,生產者生產一個,消費者消費一個

  涉及問題

  notifyAll()方法:當生產者/消費者向緩衝區放入/取出一個產品時,向其他等待的所有線程發出可執行的通知,同時放棄鎖,使自己處於等待狀態。


  再次測試代碼

 

  /**
  * Created by yuandl on 2016-10-11.
  */
  public class ProducerConsumerTest {
  public static void main(String args[]) {
  Resource resource = new Resource();
  new Thread(new Producer(resource)).start();//生产者线程
  new Thread(new Consumer(resource)).start();//消费者线程
  }
  }


運行結果

Thread-0生产者------------1
 Thread-1消费者****1
  Thread-0生产者------------2
  Thread-1消费者****2
  Thread-0生产者------------3
  Thread-1消费者****3
  Thread-0生产者------------4
  Thread-1消费者****4
  Thread-0生产者------------5
  Thread-1消费者****5
  Thread-0生产者------------6
  Thread-1消费者****6
  Thread-0生产者------------7
  Thread-1消费者****7
  Thread-0生产者------------8
  Thread-1消费者****8
  Thread-0生产者------------9
  Thread-1消费者****9
  Thread-0生产者------------10
  Thread-1消费者****10


 透過以上列印結果發現問題

 ProducerConsumerTest.java
  /**
  * Created by yuandl on 2016-10-11.
  */
  public class ProducerConsumerTest {
  public static void main(String args[]) {
  Resource resource = new Resource();
  new Thread(new Consumer(resource)).start();//生产者线程
  new Thread(new Consumer(resource)).start();//生产者线程
  new Thread(new Producer(resource)).start();//消费者线程
  new Thread(new Producer(resource)).start();//消费者线程
  }
  }

 透過以上列印結果發現問題🜀 05生產了,沒有消費

  原因分析

  當兩個線程同時操作生產者生產或者消費者消費時,如果有生產者或者的兩個線程都wait()時,再次notify(),由於其中一個線程已經改變了標記而另外一個線程再次往下直接執行的時候沒有判斷標記而導致的。

  if判斷標記,只有一次,會導致不該運行的執行緒運行了。出現了數據錯誤的情況。

  解決方案


  while判斷標記,解決了線程獲取執行權後,是否要運行!也就是每次wait()後再notify()時先再次判斷標記

  while)

  Resource.java

  Thread-0生产者------------100
  Thread-3消费者****100
  Thread-0生产者------------101
  Thread-3消费者****101
  Thread-2消费者****101
  Thread-1生产者------------102
  Thread-3消费者****102
  Thread-0生产者------------103
  Thread-2消费者****103
  Thread-1生产者------------104
  Thread-3消费者****104
  Thread-1生产者------------105
  Thread-0生产者------------106
  Thread-2消费者****106
  Thread-1生产者------------107
  Thread-3消费者****107
  Thread-0生产者------------108
  Thread-2消费者****108
  Thread-0生产者------------109
  Thread-2消费者****109
  Thread-1生产者------------110
  Thread-3消费者****110


再次發現問題



  印到某個值例如生產完74,程式運作卡死了,好像鎖死了一樣。

  原因分析

  notify:只能喚醒一個線程,如果本方喚醒了本方,沒有意義。而且while判斷標記+notify會導致」死鎖」。

  解決方案

  notifyAll解決了本方執行緒一定會喚醒對方執行緒的問題。

  最後程式碼改進(Resource中的notify()->notifyAll())


  Resource.java
 


 
 /**
  * Created by yuandl on 2016-10-11./**
  * 资源
  */
  public class Resource {
  /*资源序号*/
  private int number = 0;
  /*资源标记*/
  private boolean flag = false;
  /**
  * 生产资源
  */
  public synchronized void create() {
  while (flag) {//先判断标记是否已经生产了,如果已经生产,等待消费;
  try {
  wait();//让生产线程等待
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  }
  number++;//生产一个
  System.out.println(Thread.currentThread().getName() + "生产者------------" + number);
  flag = true;//将资源标记为已经生产
  notify();//唤醒在等待操作资源的线程(队列)
  }
  /**
  * 消费资源
  */
  public synchronized void destroy() {
  while (!flag) {
  try {
  wait();
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  }
  System.out.println(Thread.currentThread().getName() + "消费者****" + number);
  flag = false;
  notify();
  }
  }


 


 /**
  * Created by yuandl on 2016-10-11./**
  * 资源
  */
  public class Resource {
  /*资源序号*/
  private int number = 0;
  /*资源标记*/
  private boolean flag = false;
  /**
  * 生产资源
  */
  public synchronized void create() {
  while (flag) {//先判断标记是否已经生产了,如果已经生产,等待消费;
  try {
  wait();//让生产线程等待
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  }
  number++;//生产一个
  System.out.println(Thread.currentThread().getName() + "生产者------------" + number);
  flag = true;//将资源标记为已经生产
  notifyAll();//唤醒在等待操作资源的线程(队列)
  }
  /**
  * 消费资源
  */
  public synchronized void destroy() {
  while (!flag) {
  try {
  wait();
  } catch (InterruptedException e) {
  e.printStackTrace();
  }
  }
  System.out.println(Thread.currentThread().getName() + "消费者****" + number);
  flag = false;
  notifyAll();
  }
  }


〜告

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn