厨师制作食物,放在取餐口让服务员取走食物,两个角色最终共享的数据即是食物
首先模拟生产消费的过程,未加同步,所以会出现两方面的问题:
1、食物与对应描述混乱
2、本来应该每样菜品生成5次,取出的菜品数量对不上(桑拿滑肉片7次,锅包肉3次)
package com.chocus.demo1;
public class ProductCustomerDemo {
public static void main(String[] args) {
Food food = new Food();
Customers c = new Customers(food);
Producter p = new Producter(food);
Thread t1 = new Thread(c);
Thread t2 = new Thread(p);
t1.start();
t2.start();
}
}
/**
* 消费者
*
* @author Chocus
*
*/
class Customers implements Runnable {
private Food food;
public Customers(Food food) {
this.food = food;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
food.get();
}
}
}
/**
* 生产者
*
* @author Chocus
*
*/
class Producter implements Runnable {
private Food food;
public Producter(Food food) {
this.food = food;
}
@Override
public void run() {
// 模拟生成食物,单数一种,双数一种
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
food.set("锅包肉", "酸甜可口");
} else {
food.set("桑拿滑肉片", "锤儿很喜欢吃");
}
}
}
}
/**
* 食物:生产者和消费者共享的数据
*
* @author Chocus
*
*/
class Food {
/**
* 名称
*/
private String name;
/**
* 描述
*/
private String desc;
/**
* 生产食物
*/
public void set(String name, String desc) {
this.name = name;
// 模拟生成食物的过程
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.desc = desc;
}
/**
* 取走食物
*/
public void get() {
// 模拟取走食物的过程
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("取走食物" + name + ",食物信息:" + desc);
}
public Food(String name, String desc) {
this.name = name;
this.desc = desc;
}
public Food() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
结果
取走食物桑拿滑肉片,食物信息:酸甜可口
取走食物锅包肉,食物信息:锤儿很喜欢吃
取走食物桑拿滑肉片,食物信息:酸甜可口
取走食物锅包肉,食物信息:锤儿很喜欢吃
取走食物桑拿滑肉片,食物信息:酸甜可口
取走食物锅包肉,食物信息:锤儿很喜欢吃
取走食物桑拿滑肉片,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
经过简单分析可以发现,问题出在生产食物的set方法和取走食物的get方法线程不安全造成
那么在这两个方法上加同步后根据结果发现:
1、每样菜品生成5次的问题解决了,这证明加了同步后,不存在线程安全问题,及不会出现生产一半被取走的情况
2、发现出现null的情况,即还未生产菜品,菜品就被取走了
package com.chocus.demo1;
public class ProductCustomerDemo {
public static void main(String[] args) {
Food food = new Food();
Customers c = new Customers(food);
Producter p = new Producter(food);
Thread t1 = new Thread(c);
Thread t2 = new Thread(p);
t1.start();
t2.start();
}
}
/**
* 消费者
*
* @author Chocus
*
*/
class Customers implements Runnable {
private Food food;
public Customers(Food food) {
this.food = food;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
food.get();
}
}
}
/**
* 生产者
*
* @author Chocus
*
*/
class Producter implements Runnable {
private Food food;
public Producter(Food food) {
this.food = food;
}
@Override
public void run() {
// 模拟生成食物,单数一种,双数一种
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
food.set("锅包肉", "酸甜可口");
} else {
food.set("桑拿滑肉片", "锤儿很喜欢吃");
}
}
}
}
/**
* 食物:生产者和消费者共享的数据
*
* @author Chocus
*
*/
class Food {
/**
* 名称
*/
private String name;
/**
* 描述
*/
private String desc;
/**
* 生产食物
*/
public synchronized void set(String name, String desc) {
this.name = name;
// 模拟生成食物的过程
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.desc = desc;
}
/**
* 取走食物
*/
public synchronized void get() {
// 模拟取走食物的过程
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("取走食物" + name + ",食物信息:" + desc);
}
public Food(String name, String desc) {
this.name = name;
this.desc = desc;
}
public Food() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
结果
取走食物null,食物信息:null
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物锅包肉,食物信息:酸甜可口
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
根据问题分析可以发现,菜品还未生产就被取走,那么就需要先生产菜品,生产完毕后再取走
package com.chocus.demo1;
public class ProductCustomerDemo {
public static void main(String[] args) {
Food food = new Food();
Customers c = new Customers(food);
Producter p = new Producter(food);
Thread t1 = new Thread(c);
Thread t2 = new Thread(p);
t1.start();
t2.start();
}
}
/**
* 消费者
*
* @author Chocus
*
*/
class Customers implements Runnable {
private Food food;
public Customers(Food food) {
this.food = food;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
food.get();
}
}
}
/**
* 生产者
*
* @author Chocus
*
*/
class Producter implements Runnable {
private Food food;
public Producter(Food food) {
this.food = food;
}
@Override
public void run() {
// 模拟生成食物,单数一种,双数一种
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
food.set("锅包肉", "酸甜可口");
} else {
food.set("桑拿滑肉片", "锤儿很喜欢吃");
}
}
}
}
/**
* 食物:生产者和消费者共享的数据
*
* @author Chocus
*
*/
class Food {
/**
* 名称
*/
private String name;
/**
* 描述
*/
private String desc;
/**
* 定义生产消费标记:
* true:可以消费,不能生产;
* false:可以生产,不能消费;
* 注意:开始时要设置为不能消费!!!!
*/
private boolean flag = false;
/**
* 生产食物
*/
public synchronized void set(String name, String desc) {
// 生产的食物还没有被取走,那么先进入等待状态,停止生产
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 模拟生成食物的过程
this.name = name;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.desc = desc;
// 产品生产完毕后通知消费者来消费
flag = true;
// notifyAll:唤醒休眠中的其他所有线程
// notify:唤醒休眠中的某一个线程(随机)
this.notifyAll();
}
/**
* 取走食物
*/
public synchronized void get() {
// 当没有食物的食物不能消费,进入等待状态
if (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 模拟取走食物的过程
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("取走食物" + name + ",食物信息:" + desc);
// 取走食物后通知生产者生产食物
flag = false;
// notifyAll:唤醒休眠中的其他所有线程
// notify:唤醒休眠中的某一个线程(随机)
this.notifyAll();
}
public Food(String name, String desc) {
this.name = name;
this.desc = desc;
}
public Food() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
结果 5:5
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
现在加大力度,两个生产者,两个消费者
public class ProductCustomerDemo {
public static void main(String[] args) {
Food food = new Food();
Customers c = new Customers(food);
Producter p = new Producter(food);
Thread t1 = new Thread(c);
Thread t2 = new Thread(p);
Thread t3 = new Thread(c);
Thread t4 = new Thread(p);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
结果 10:10
取走食物锅包肉,食物信息:酸甜可口
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
一个生产者,两个消费者
public static void main(String[] args) {
Food food = new Food();
Customers c = new Customers(food);
Producter p = new Producter(food);
Thread t1 = new Thread(c);
Thread t2 = new Thread(p);
Thread t3 = new Thread(c);
t1.start();
t2.start();
t3.start();
}
结果 5:5,一个人生产,两个人排队领取食物
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
两个生产者,一个消费者
public static void main(String[] args) {
Food food = new Food();
Customers c = new Customers(food);
Producter p = new Producter(food);
Thread t1 = new Thread(c);
Thread t2 = new Thread(p);
Thread t4 = new Thread(p);
t1.start();
t2.start();
t4.start();
}
结果 6:4,两个人竞争生产,一个人来领取,可以看出,又出现了线程安全问题,即两个人同时在生成中
取走食物锅包肉,食物信息:酸甜可口
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物锅包肉,食物信息:酸甜可口
根据上述问题,可以找到解决方案
方案一:即在单双数生产食物过程中加上同步即可,但是此种方案可能出现死锁。。。可能。。。所以推荐第二种方案
/**
* 生产者
*
* @author Chocus
*
*/
class Producter implements Runnable {
private Food food;
public Producter(Food food) {
this.food = food;
}
@Override
public void run() {
product();
}
private synchronized void product() {
// 模拟生成食物,单数一种,双数一种
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
food.set("锅包肉", "酸甜可口");
} else {
food.set("桑拿滑肉片", "锤儿很喜欢吃");
}
}
}
}
结果 5:5
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
方案二:生产者和消费者都只唤醒一个线程,即让一个人来做,做好了通知一个人来领
/**
* 食物:生产者和消费者共享的数据
*
* @author Chocus
*
*/
class Food {
/**
* 名称
*/
private String name;
/**
* 描述
*/
private String desc;
/**
* 定义生产消费标记:
* true:可以消费,不能生产;
* false:可以生产,不能消费;
* 注意:开始时要设置为不能消费!!!!
*/
private boolean flag = false;
/**
* 生产食物
*/
public synchronized void set(String name, String desc) {
// 生产的食物还没有被取走,那么先进入等待状态,停止生产
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 模拟生成食物的过程
this.name = name;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.desc = desc;
// 产品生产完毕后通知消费者来消费
flag = true;
// notifyAll:唤醒休眠中的其他所有线程
// notify:唤醒休眠中的某一个线程(随机)
this.notify();
}
/**
* 取走食物
*/
public synchronized void get() {
// 当没有食物的食物不能消费,进入等待状态
if (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 模拟取走食物的过程
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("取走食物" + name + ",食物信息:" + desc);
// 取走食物后通知生产者生产食物
flag = false;
// notifyAll:唤醒休眠中的其他所有线程
// notify:唤醒休眠中的某一个线程(随机)
this.notify();
}
public Food(String name, String desc) {
this.name = name;
this.desc = desc;
}
public Food() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
结果:5:5
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
取走食物锅包肉,食物信息:酸甜可口
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
取走食物锅包肉,食物信息:酸甜可口
根据以上知识总结: