java中 wait notify實現生產者-消費者問題

只有一個商品的狀況java

1.一輩子產者一消費者

首先定義倉庫類MyStack,用來存儲商品ide

package com.feng.example;

import java.util.ArrayList;
import java.util.List;
/**
 * 生產者與消費者問題的倉庫
 * 要求倉庫只有一件商品
 * @author feng
 *
 */
public class MyStack {
	
	private List<String> list = new ArrayList<String>();
	
	synchronized public void pop()
	{
		try {
			
			if(list.size() == 0)
			{
				this.wait();  //這裏使用this,若是使用list.wait()須要使用synchronized語句對list加鎖
			}
			System.out.println("銷售商品");
			list.remove(0);
			this.notify();
		}
		 catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	}
	
	synchronized public void push()
	{
		try {
			
			if(list.size() != 0)
			{
				this.wait();
			}
			System.out.println("生產商品");
			list.add("商品");
			this.notify();
		}catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	}

}

定義生產者類Producer測試

package com.feng.example;

public class Producer extends Thread{

	private MyStack myStack;
	
	public Producer(MyStack myStack)
	{
		this.myStack = myStack;
	}

	@Override
	public void run() {
		
		while(true)
		{
			myStack.push();
		}
	}
	
	
}

定義消費者類:this

package com.feng.example;

public class Customer extends Thread{
	
	private MyStack myStack;
	
	public  Customer(MyStack myStack)
	{
		this.myStack = myStack;
	}
	
	public void run()
	{
		while(true)
		{
			myStack.pop();
		}
	}

}

定義測試類:
spa

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */

	
	public static void main(String[] args) {
			
		MyStack myStack = new MyStack();
		
		Thread producer = new Producer(myStack);
		Thread customer = new Customer(myStack);
		
		producer.start();
		customer.start();
		
	}

}

分析:在操做棧MyStack中,push和pop方法都是synchronized方法,也就是線程producer和customer同一時刻只會有一個線程去執行操做棧,使用wait,notify也保證了push老是在pop前執行,所以實驗結果是生產商品,出售商品交替打印。
線程

運行結果:code

生產商品
銷售商品
生產商品
銷售商品
生產商品
銷售商品
生產商品
銷售商品
生產商品
銷售商品

2.一輩子產者多消費者

修改測試類代碼,將測試類代碼修改成一個生產者,5個消費者的狀況
進程

修改代碼以下:rem

package com.feng.example;

public class ThreadTest {

	/**
	 * @param args
	 */

	
	public static void main(String[] args) {
			
		MyStack myStack = new MyStack();
		
		Thread producer = new Producer(myStack);
		
		Thread customer1 = new Customer(myStack);
		Thread customer2 = new Customer(myStack);
		Thread customer3 = new Customer(myStack);
		Thread customer4 = new Customer(myStack);
		Thread customer5 = new Customer(myStack);
		
		customer1.start();
		customer2.start();
		customer3.start();
		customer4.start();
		customer5.start();
		
		producer.start();
		
	}

}

運行程序,先查看運行結果:it

生產商品
銷售商品
生產商品
銷售商品
銷售商品
銷售商品
Exception in thread "Thread-5" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
	at java.util.ArrayList.RangeCheck(ArrayList.java:547)
	at java.util.ArrayList.remove(ArrayList.java:387)
	at com.feng.example.MyStack.pop(MyStack.java:24)
	at com.feng.example.Customer.run(Customer.java:16)
Exception in thread "Thread-4" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
	at java.util.ArrayList.RangeCheck(ArrayList.java:547)
	at java.util.ArrayList.remove(ArrayList.java:387)
	at com.feng.example.MyStack.pop(MyStack.java:24)
	at com.feng.example.Customer.run(Customer.java:16)

分析:從結果中能夠看出,連續銷售了三件商品,可是咱們的倉庫只能放一件商品,連續銷售三件商品確定是不現實的。引發這個問題的緣由重要在於:喚醒了同類進程。也就是說消費者本應該喚醒生產者線程,卻喚醒的是消費者線程。因此會致使程序的錯誤。所以咱們須要在每次喚醒後還要判斷一次是否知足了操做的條件。所以咱們應該講MyStack類中的if改成while,修改程序以下:

package com.feng.example;

import java.util.ArrayList;
import java.util.List;
/**
 * 生產者與消費者問題的倉庫
 * 要求倉庫只有一件商品
 * @author feng
 *
 */
public class MyStack {
	
	private List<String> list = new ArrayList<String>();
	
	synchronized public void pop()
	{
		try {
			
			while(list.size() == 0)
			{
				this.wait();  //這裏使用this,若是使用list.wait()須要使用synchronized語句對list加鎖
			}
			System.out.println("銷售商品");
			list.remove(0);
			this.notify();
		}
		 catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	}
	
	synchronized public void push()
	{
		try {
			
			while(list.size() != 0)
			{
				this.wait();
			}
			System.out.println("生產商品");
			list.add("商品");
			this.notify();
		}catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	}

}

運行程序,查看結果:

生產商品
銷售商品   //程序應經不輸出了,然而咱們的代碼是while(true),不輸出確定存在的問題

從輸出結果中能夠看出,咱們的程序還存在死鎖的問題,死鎖的問題是怎麼解決的。 描述這類問題可能出現的狀況,如今有五個消費者線程,customer1,customer2,customer3,customer4,customer5假如如今5個線程都走到while(list.size() ==0),五個線程都wait阻塞。生產者線程producor,生產一個商品,執行notify操做,隨機喚醒一個消費者線程(執行完notify操做後,因爲是一個無限循環,producor線程在while(list.size != 0)處執行了wait阻塞)假設喚醒了customer1,customer1銷售一個商品。本應該喚醒producor線程的,缺隨機喚醒了一個個消費者線程假如是customer2線程,customer2線程被喚醒以後從新執行while(list.size == 0 ),因爲條件成立,再次wait阻塞,如今全部的線程都處於阻塞狀態,也就是形成了死鎖。

解決方案就是將notify操做改成notifyAll,這在效率上確定有低,後期再講解怎麼才能高效。

修改MyStack類:

package com.feng.example;

import java.util.ArrayList;
import java.util.List;
/**
 * 生產者與消費者問題的倉庫
 * 要求倉庫只有一件商品
 * @author feng
 *
 */
public class MyStack {
	
	private List<String> list = new ArrayList<String>();
	
	synchronized public void pop()
	{
		try {
			
			while(list.size() == 0)
			{
				this.wait();  //這裏使用this,若是使用list.wait()須要使用synchronized語句對list加鎖
			}
			System.out.println("銷售商品");
			list.remove(0);
			this.notifyAll();
		}
		 catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	}
	
	synchronized public void push()
	{
		try {
			
			while(list.size() != 0)
			{
				this.wait();
			}
			System.out.println("生產商品");
			list.add("商品");
			this.notifyAll();
		}catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	}

}

運行結果:程序應經可以正常運行了

生產商品
銷售商品
生產商品
銷售商品
生產商品
銷售商品

3.多生產者一消費者(同上)

4.多生產者多消費者(同上)

對於倉庫中能夠存放多件商品的問題,只需修改push的壓棧條件便可,也就是知道何時爲滿,好比定義倉庫最多能夠放n件商品,只需修改判斷條件爲:while(list.size() >n){ this.wait(); }便可

相關文章
相關標籤/搜索