生产者与消费者问题引出
有这么一个类Info,它拥有两个属性:name和desc。name代表名字,desc代表描述信息。有一个生产类Producer,专门生产Info对象,为其设置name和desc属性值;还有一个消费者类Customer,专门消费Info对象,读取Info对象的name和desc值。生产类和消费者类各自的线程启动。以下是代码:
class Info{
private String name;
private String desc;
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;
}
}
class Producer implements Runnable{
private Info info;
public Producer(Info info) {
this.info = info;
}
@Override
public void run() {
for (int i=0;i<50;i++){
if (i%2==0){
info.setName("小明");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
info.setDesc("三好学生");
}else {
info.setName("pig");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
info.setDesc("一只猪");
}
}
}
}
class Customer implements Runnable{
private Info info;
public Customer(Info info) {
this.info = info;
}
@Override
public void run() {
for (int i=0;i<20;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(info.getName()+"--"+info.getDesc());
}
}
}
public class Main{
public static void main(String[] args) {
Info info=new Info();
Producer producer=new Producer(info);
Customer customer=new Customer(info);
new Thread(producer).start();
new Thread(customer).start();
}
}
运行结果:
pig--三好学生
pig--一只猪
pig--三好学生
pig--一只猪
小明--一只猪
小明--一只猪
pig--三好学生
小明--一只猪
pig--三好学生
小明--一只猪
小明--一只猪
小明--一只猪
pig--三好学生
小明--一只猪
小明--三好学生
pig--一只猪
小明--一只猪
pig--一只猪
pig--三好学生
小明--一只猪
问题出现了,生产者明明是生产:小明--三好学生和pig--一只猪的,怎么消费者取出来变成了小明--一只猪或者pig--三好学生了呢?问题在于Producer在setName()之后休眠了100毫秒。比如Producer前一个生产的Info对象是小明--三好学生,现在它刚把name设置成pig,然后进入休眠,还没来得及设置desc,Customer就已经开始读取Info了,自然读出来的就是pig--三好学生了。
同步解决问题
以上的问题是由于设置info和取出info不是同步操作,所以我们可以使其同步,修改Producer与Customer类:
class Producer implements Runnable{
private Info info;
public Producer(Info info) {
this.info = info;
}
@Override
public void run() {
for (int i=0;i<50;i++){
if (i%2==0){
synchronized (info){
info.setName("小明");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
info.setDesc("三好学生");
}
}else {
synchronized (info){
info.setName("pig");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
info.setDesc("一只猪");
}
}
}
}
}
class Customer implements Runnable{
private Info info;
public Customer(Info info) {
this.info = info;
}
@Override
public void run() {
for (int i=0;i<20;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (info){
System.out.println(info.getName()+"--"+info.getDesc());
}
}
}
}
运行结果:
小明--三好学生
pig--一只猪
小明--三好学生
小明--三好学生
pig--一只猪
小明--三好学生
小明--三好学生
pig--一只猪
小明--三好学生
pig--一只猪
小明--三好学生
pig--一只猪
小明--三好学生
小明--三好学生
pig--一只猪
小明--三好学生
小明--三好学生
pig--一只猪
pig--一只猪
小明--三好学生
利用syncronized,就解决了数据错位问题。但是,继续延伸问题,如何实现生产者生产一个info对象,消费者读取一个info对象,而不出现重复读取info的现象呢?
利用wait()和notify()解决重复问题
加入一个标志量flag,当生产者生产一个info,将其设置为true,当消费者消费一个info,将其设置为false。当flag=true的时候,生产者wait(),当flag=false的时候,消费者wait()。
class Info{
private String name;
private String desc;
private boolean flag=false;
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
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;
}
}
class Producer implements Runnable{
private Info info;
public Producer(Info info) {
this.info = info;
}
@Override
public void run() {
for (int i=0;i<50;i++){
if (i%2==0){
synchronized (info){
if (info.isFlag()){
try {
info.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
info.setName("小明");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
info.setDesc("三好学生");
info.setFlag(true);
try {
info.notify();
} catch (Exception e) {
e.printStackTrace();
}
}
}else {
synchronized (info){
if (info.isFlag()){
try {
info.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
info.setName("pig");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
info.setDesc("一只猪");
info.setFlag(true);
try {
info.notify();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
class Customer implements Runnable{
private Info info;
public Customer(Info info) {
this.info = info;
}
@Override
public void run() {
for (int i=0;i<20;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (info){
if (!info.isFlag()){
try {
info.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(info.getName()+"--"+info.getDesc());
info.setFlag(false);
try {
info.notify();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
以上代码只是为解决问题而写的,并没有优化,最好是在Info类里设置同步以及wait()和notify()。