多线程之生产者与消费者


多线程中有个很经典的,关于生产者与消费者的的问题,大致就是,有多个生产者生产同一种产品,另外有多个消费者消费这个产品,当生产者生产到一定程度时停止生产,这时通知消费者赶快消费,当消费者把产品消费完以后停止消费,并通知生产者开始生产。

 映射到程序,我是这样设计的,有一个生产者的线程类(Producter),以及消费者的线程类(Consumer),还有一个产品类(Product),以及提供了对产品进行管理的类(ProducerAndConsumer),里面有一个产品的数组,并对外提供了添加产品,消费产品的方法(addProduct、getProduct),考虑到多个线程访问资源的安全,这两个方法都做了同步,保证一个时间点只有一个线程使用资源

具体代码如下:

Product类,每个产品都有一个id,并重写了toString方法

//产品类
class Product {
 
//产品id
 private int id;
 
 public Product(int id) {
  this.id = id;
 }
 @Override
 public String toString() {
  return "id=" + id;
 }
}

生产者类(Producter),从Thread继承,保留一个资源管理类ProducerAndConsumer 的引用

class Producer extends Thread{
 
//保留ProducerAndConsumer的引用
 private ProducerAndConsumer pac; 
 public Producer(ProducerAndConsumer pac) {
  this.pac = pac;
 }
 @Override
 public void run() {
  System.out.println("Producer run");
  Random r = new Random();//随机数生成器,
  int index = 0;
  //生产15个产品
  while (index++ < 15) {
   Product product = new Product(index);//new出一个id为i的产品
   pac.addProduct(product);//向pac添加一个产品,就是生产一个产品
   System.out.println(Thread.currentThread().getName() + " produce product " + product);
   try {
    Thread.sleep(r.nextInt(1000));//随机休眠0至一秒,休眠时不放开锁
   } catch (InterruptedException e) {
    e.printStackTrace();
   }  
  }
 }
}

消费者类(Consumer),和生产者类一样,也是从Thread继承,保留一个资源管理类ProducerAndConsumer 的引用

class Consumer extends Thread {
 
//保留ProducerAndConsumer的引用
 private ProducerAndConsumer pac;
 
 public Consumer(ProducerAndConsumer pac) {
  this.pac = pac;
 }
 
 @Override
 public void run() {
  System.out.println("Consumer run");
  Random r = new Random();
  int index = 15;
  //消费15个产品
  while (index-- > 0) { 
   Product product = pac.getProduct();//问pac要一个产品,消费掉
   System.out.println(Thread.currentThread().getName() + " consume product " + product);
   try {
    Thread.sleep(r.nextInt(2000));//随机休眠0至2秒,休眠时不放开锁
   } catch (InterruptedException e) {
    e.printStackTrace();
   }  
  }
 }
}

资源管理的类(ProducerAndConsumer),也是main方法所在类,主要提供了两个对产品操作的方法

/**
 
*
多线程同步经典问题,生产者和消费者,
 
* @author my
 *
 */
public class ProducerAndConsumer {
 
 //记录目前的索引
 private int index = 0;
 //产品的容器,容量是10个产品
 private Product[] products = new Product[10];
 
 public static void main(String[] args) { 
  ProducerAndConsumer pac = new ProducerAndConsumer();
  new Producer(pac).start();
  new Consumer(pac).start();
 }
 
 //像容器中添加产品,此方法需要同步,以防止多线程访问造成的资源不一致
 public synchronized void addProduct(Product product) {
  //条件反转,拒绝添加空的产品
  if (product == null) {
   return;
  }
  //若目前的索引等于products的长度,把生产者线程暂停,并唤醒消费者线程
  if (products.length == index) {
   try {
    this.wait();//暂停生产者线程   
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
  }  
  this.notify(); //唤醒消费者线程去消费产品
  //把产品添加到products的index索引处
  products[index] = product;
  //把索引加1
  index ++;
 }
 
 //得到容器中的产品,此方法需要同步,以防止多线程访问造成的资源不一致
 public synchronized Product getProduct() {
  //若没有产品了,就不在消费
  if (index == 0) {
   try {
    this.wait();//暂停消费   
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
  } 
  this.notify();//唤醒生产者,生产产品
  index--;//把索引减去1
  //返回索引处的产品
  return products[index];
 }
}

运行结果:

Producer run
Consumer run
Thread-0 produce product id=1
Thread-1 consume product id=1
Thread-0 produce product id=2
Thread-1 consume product id=2
Thread-0 produce product id=3
Thread-1 consume product id=3
Thread-0 produce product id=4
Thread-1 consume product id=4
Thread-0 produce product id=5
Thread-0 produce product id=6
Thread-0 produce product id=7
Thread-1 consume product id=7
Thread-0 produce product id=8
Thread-0 produce product id=9
Thread-0 produce product id=10
Thread-1 consume product id=10
Thread-0 produce product id=11
Thread-1 consume product id=11
Thread-0 produce product id=12
Thread-0 produce product id=13
Thread-1 consume product id=13
Thread-0 produce product id=14
Thread-0 produce product id=15
Thread-1 consume product id=15
Thread-1 consume product id=14
Thread-1 consume product id=12
Thread-1 consume product id=9
Thread-1 consume product id=8
Thread-1 consume product id=6
Thread-1 consume product id=5

程序成功模拟了这个过程,但是出现了一个非常奇怪的问题,刚开始Producter、Consumer,这两个类是实现Runnable接口,结果是消费者类无法启动,改为从Thread继承后,程序正常了,真是一个非常奇怪的问题,查了资料、问了群里的兄弟都无解,无语。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值