只有一个商品的状况java
首先定义仓库类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
生产商品 销售商品 生产商品 销售商品 生产商品 销售商品 生产商品 销售商品 生产商品 销售商品
修改测试类代码,将测试类代码修改成一个生产者,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(); } } }
运行结果:程序应经可以正常运行了
生产商品 销售商品 生产商品 销售商品 生产商品 销售商品
对于仓库中能够存放多件商品的问题,只需修改push的压栈条件便可,也就是知道何时为满,好比定义仓库最多能够放n件商品,只需修改判断条件为:while(list.size() >n){ this.wait(); }便可