/*
* 模拟生产者和消费者:
* 思路:
* 资源类:包子类
* 生产者线程:SetThread
* 消费者线程:GetThread
* 测试类:Demo6
*
* 我们按照思路写出了代码,发现了有null和0这样的数据
* 因为线程是抢占式争夺cpu的执行权的,有可能消费者线程先抢到了cpu的执行权,那么这个时候
* 生产者线程还没有生产出来,所以就出现了null 和 0 这样的数据。
* 很明显这样是不对的,应该是生产者先生产,然后消费者在消费。
*
* 为了让效果更明显一些,我加入循环和判断,给出不同的值
* 结果又出现了新的问题:
* 同一个包子被消费了多次
* 因为cpu的一点点的执行权,就足够执行这个代码很多次。
* 包子和价格不匹配了
* 因为线程运行的随机性
*
* 说明存在线程安全问题:
* 是否是多线程:是
* 是否有共享数据:有
* 是否有多条语句操作共享数据:有
*
* 解决方案:
* 上锁
* 注意:
* 不同种类的线程都要加锁,而且必须是同一把锁
*
* 线程安全问题通过加锁是解决了,但是还存在一个问题:
* 如果是消费者先抢到了cpu的执行权,就会去消费,但是这个时候生产者还没有生产,消费者消费的数据就是默认值 null 和 0
* 应该等生产者生产之后再消费。这样才有意义。
* 如果生产者先抢到了执行权,就去生产,但是它生产完成后有肯能还拥有执行权,它就会继续生产,这个也是有问题的。
* 应该是生产完后等消费者把它消费掉后,再继续生产。
*
* 正常的情况:
* 生产者:
* 先看是否有数据了,如果有,就等着消费者来消费。如果没有,就生产。生产完后通知消费者来消费包子。。。
* 消费者:
* 先看是否有数据,如果有,就消费。如果没有,就等待,通知生产者去生产。
*
* 为了解决这个问题,java就提供了一种机制: 等待唤醒机制。
*
* 等待唤醒机制:
* Object类中有3个方法:
* public final void wait()
public final void notify()
public final void notifyAll()
为什么这几个方法不在Thread中?
因为这个方法的调用是通过锁对象调用的,而我们同步代码块的锁对象可以是任意对象,
所以,这些方法就定义再Object中。
* */
public class Demo6 {
public static void main(String[] args) {
//创建资源对象
BaoZi bz = new BaoZi();
//创建生产者和消费者
SetThread st = new SetThread(bz);
GetThread gt = new GetThread(bz);
//创建线程
Thread t1 = new Thread(st,"成产者");
Thread t2 = new Thread(gt,"消费者");
t2.setPriority(10);
//启动线程
t2.start();
t1.start();
}
}
package com.momo.demo;
public class GetThread implements Runnable {
private BaoZi bz;
public GetThread(BaoZi bz) {
this.bz = bz;
}
@Override
public void run() {
// BaoZi bz = new BaoZi();
while (true) {
synchronized (bz) {
System.out.println(bz.name + ":" + bz.price);
// System.out.println(Thread.currentThread().getName()+"消费了这个包子");
}
}
}
}
package com.momo.demo;
public class SetThread implements Runnable {
private BaoZi bz;
private int i = 0;
public SetThread(BaoZi bz) {
this.bz = bz;
}
@Override
public void run() {
// BaoZi bz = new BaoZi();
while (true) {
synchronized (bz) {
if (i % 2 == 0) {
bz.name = "肉包子";
bz.price = 2;
// System.out.println(Thread.currentThread().getName()+"生产了一个肉包子");
} else {
bz.name = "菜包子";
bz.price = 1;
// System.out.println(Thread.currentThread().getName()+"生产了一个菜包子");
}
// System.out.println(Thread.currentThread().getName()+"生产了一个包子");
i++;
}
}
}
}
package com.momo.demo;
public class BaoZi {
String name;
int price;
}
package com.momo.demo;
/*
* 模拟生产者和消费者:
* 思路:
* 资源类:包子类
* 生产者线程:SetThread
* 消费者线程:GetThread
* 测试类:Demo6
*
* 我们按照思路写出了代码,发现了有null和0这样的数据
* 因为线程是抢占式争夺cpu的执行权的,有可能消费者线程先抢到了cpu的执行权,那么这个时候
* 生产者线程还没有生产出来,所以就出现了null 和 0 这样的数据。
* 很明显这样是不对的,应该是生产者先生产,然后消费者在消费。
*
* 为了让效果更明显一些,我加入循环和判断,给出不同的值
* 结果又出现了新的问题:
* 同一个包子被消费了多次
* 因为cpu的一点点的执行权,就足够执行这个代码很多次。
* 包子和价格不匹配了
* 因为线程运行的随机性
*
* 说明存在线程安全问题:
* 是否是多线程:是
* 是否有共享数据:有
* 是否有多条语句操作共享数据:有
*
* 解决方案:
* 上锁
* 注意:
* 不同种类的线程都要加锁,而且必须是同一把锁
*
* 线程安全问题通过加锁是解决了,但是还存在一个问题:
* 如果是消费者先抢到了cpu的执行权,就会去消费,但是这个时候生产者还没有生产,消费者消费的数据就是默认值 null 和 0
* 应该等生产者生产之后再消费。这样才有意义。
* 如果生产者先抢到了执行权,就去生产,但是它生产完成后有肯能还拥有执行权,它就会继续生产,这个也是有问题的。
* 应该是生产完后等消费者把它消费掉后,再继续生产。
*
* 正常的情况:
* 生产者:
* 先看是否有数据了,如果有,就等着消费者来消费。如果没有,就生产。生产完后通知消费者来消费包子。。。
* 消费者:
* 先看是否有数据,如果有,就消费。如果没有,就等待,通知生产者去生产。
*
* 为了解决这个问题,java就提供了一种机制: 等待唤醒机制。
*
* 等待唤醒机制:
* Object类中有3个方法:
* public final void wait()
public final void notify()
public final void notifyAll()
为什么这几个方法不在Thread中?
因为这个方法的调用是通过锁对象调用的,而我们同步代码块的锁对象可以是任意对象,
所以,这些方法就定义再Object中。
* */
public class Demo6 {
public static void main(String[] args) {
//创建资源对象
BaoZi bz = new BaoZi();
//创建生产者和消费者
SetThread st = new SetThread(bz);
GetThread gt = new GetThread(bz);
//创建线程
Thread t1 = new Thread(st,"成产者");
Thread t2 = new Thread(gt,"消费者");
t2.setPriority(10);
//启动线程
t2.start();
t1.start();
}
}
package com.momo.demo;
public class GetThread implements Runnable {
private BaoZi bz;
public GetThread(BaoZi bz) {
this.bz = bz;
}
@Override
public void run() {
// BaoZi bz = new BaoZi();
while (true) {
synchronized (bz) {
if(!bz.boo) {//false !false= true !true = false
try {
bz.wait();//t2就等待,会释放锁对象.将来醒来的时候,是从这里醒过来的。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+":"+bz.name + ":" + bz.price);
// System.out.println(Thread.currentThread().getName()+"消费了这个包子");
//修改标记,唤醒线程
bz.boo = false;
bz.notify();//唤醒t1
}
}
}
}
package com.momo.demo;
public class SetThread implements Runnable {
private BaoZi bz;
private int i = 0;
public SetThread(BaoZi bz) {
this.bz = bz;
}
@Override
public void run() {
// BaoZi bz = new BaoZi();
while (true) {
synchronized (bz) {
//判断有没有
if(bz.boo) {//false true
try {
bz.wait();//t1等待,释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (i % 2 == 0) {
bz.name = "肉包子";
bz.price = 2;
System.out.println(Thread.currentThread().getName()+"生产了一个肉包子");
} else {
bz.name = "菜包子";
bz.price = 1;
System.out.println(Thread.currentThread().getName()+"生产了一个菜包子");
}
// System.out.println(Thread.currentThread().getName()+"生产了一个包子");
i++;
//修改标记,唤醒线程
bz.boo = true;
bz.notify(); //唤醒t2,唤醒后并不是马上就执行了,还是要抢cpu的执行权。
}
}
}
}
package com.momo.demo;
public class BaoZi {
String name;
int price;
boolean boo;//默认值是false,代表没有,如果是true了,就代表有了
}