package org.demo.thread;
public class ProduceConSumer {
/**
*生产者和消费者的关系
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
// 构建一个篮子
SyncStack ss = new SyncStack();
// 构建一个生产者
producer producer = new producer(ss);
// 构建一个消费额在
Consumer consumer = new Consumer(ss);
// :两个线程访问的是同一个篮子
new Thread(producer).start();// 生产者的线程启动
new Thread(consumer).start();// 消费者的线程启动
// 注:这里可以多个生产者一个消费者,但是完美的效果是一个生产者,对应一生产者,不然就在消费者那里消费的数目等于生产者生产的数目
// new Thread(producer).start();
// new Thread(producer).start();
// new Thread(producer).start();
// new Thread(producer).start();
}
}
// 馒头
class WoTou {
int id;
public WoTou(int id) {
this.id = id;
}
// 重写 toString 是为了看到消费者
@Override
public String toString() {
// TODO Auto-generated method stub
return "WoTou:" + id;
}
}
// 装馒头的篮子,先进后出
class SyncStack {
int index = 0;
/**
* 用于装馒头的容量
*/
WoTou[] arrWT = new WoTou[6];
/**
* 用于装馒头的容量 装馒头 注意仔细想想为什么加synchronized 考虑当篮子装满了的时候,index会超出索引报错,解决办法 wait
*
* @throws InterruptedException
*/
public synchronized void push(WoTou wt) {
// 当index=篮子的长度的时候,篮子满了
while (index == arrWT.length) {// 注意这里永远是while
/**
* 注意这里抛异常,wait不是线程的, wait是Object的里面的方法 对wait的解释:当前的正在访问我这个对象的线程wait
* wait和sleep的区别:
* (wait过后synchronized不在拥有,sleep后,睡着了还是拥有synchronized)
*/
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 当wait完过后是需要叫醒wait的,notify和wait一般都是一一对应
// notify的作用叫醒一个正在wait的线程的对象,让线程继续执行
this.notify();
// this.notifyAll();//顾名思义,有多个的时候调用这个
arrWT[index] = wt;
index++;
}
// 拿出一个馒头
public synchronized WoTou pop() {
// 消费完了,篮子没货了,这里wait和上面一样
while (index == 0) {// 注意这里永远只能是while
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notify();// 注意:当注释掉这里的时候,没叫醒正在wait的线程,观察一下数据的结果
// this.notifyAll();//顾名思义,有多个的时候调用这个
index--;
return arrWT[index];
}
}
// 生产者
class producer implements Runnable {
SyncStack ss = null;// 因为生产者,要往篮子仍东西,所有拥有篮子
public producer(SyncStack ss) {
this.ss = ss;
}
@Override
public void run() {
// 模拟每个人生产20个馒头
for (int i = 0; i < 20; i++) {
WoTou wt = new WoTou(i);
ss.push(wt);// 把馒头扔进篮子,生产方法
System.out.println("生产了:" + wt);
try {
// Thread.sleep(1000);// 为了观察数据,让它每生产一个睡眠一秒
// 随机随眠,这里把生产的时间速度快点,有助于观察数据,
// 当消费玩完了的时候,消费者没有告诉它,没有叫醒wait,这是在注释掉this.notify()的时候,就一直等待,也算线程的死锁
// 这里还同理消费这的this.notify(),所以在生产者和消费者中的this.notify()这两个都是不能缺少的
Thread.sleep((int) (Math.random() * 200));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
// 消费者
class Consumer implements Runnable {
SyncStack ss = null;// 消费者从篮子拿馒头
public Consumer(SyncStack ss) {
this.ss = ss;
}
@Override
public void run() {
// 模拟每个人吃20个馒头
for (int i = 0; i < 20; i++) {
WoTou wt = ss.pop();
System.out.println("消费了:" + wt);
try {
// Thread.sleep(1000);
Thread.sleep((int) (Math.random() * 1000));// 随机随眠
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
/**
* 注:wait()和sleep()的区别:
* wait方法是Object的方法,都能让线程停止一会,wait停止是相当于把锁放开了,别人可以访问
* sleep是线程的方法,都能让线程停止一会,sleep停止的时候,仍然抱着锁,别人无法访问,
*
*/