多线程之生产者和消费者模型

  • 生产者-消费者问题简单描述:
    即生产者生产一种商品,消费者来消费这类商品。假如:存放商品的仓库容量有限,生产者就不能不停的生产商品,他得等待消费者消费商品。而对于消费者而言,在生产者没有生产任何商品时,不能消费。
  • 下面以一个线程设置某个学生的属性,而另一个线程来读取属性值来模拟生产者和消费者模型。
  • 先创建操作的资源类(学生类)
public class Student {

	String name;
	int age;
}
  • 创建生产者和消费者类
public class setThread implements Runnable {

	private Student s;
	private int i=0;
	public setThread(Student s) {
		this.s=s;
	}
	@Override
	public void run() {
		while(true) {
			if(i%2==0) {
				s.name="张三";
				s.age=20;
			}else {
				s.name="李四";
				s.age=22;
			}
			i++;
		}
	}

}
  • 测试类
public class getThread implements Runnable {

	private Student s;
	public getThread(Student s) {
		this.s=s;
	}
	@Override
	public void run() {
		while(true) {
			System.out.println(s.name+":"+s.age);
		}
	}
}

运行结果:

李四:20
李四:20
李四:22
李四:20
李四:22
李四:20
张三:22
李四:20
李四:20
张三:20
张三:20
张三:20
张三:20
  • 当我们停掉程序,得到的并不是我们想要的结果(如:把李四的年龄设置成了20,张三的却变成了22),线程之间通信出现问题了。可以看到运行结果中值混乱了。所以说明在程序运行过程中负责生产的线程还没生产,或者说还没生产完时,消费者线程就取值了,也存在消费者在取值时还没取完,生产者就开始重新设置值,导致程序出现了我们预料之外的结果。显然需要对不同线程之间的通信进行一定的控制,才能保证程序正确的运行。
  • 解决方案一(对不同的线程进行加锁,使用synchronized,注意在加锁的时候必须使用相同的锁对象,我们选择student对象s作为锁对象)改进的代码如下:
  • 对共享数据加锁后的代码
public class setThread implements Runnable {

	private Student s;
	private int i=0;
	public setThread(Student s) {
		this.s=s;
	}
	@Override
	public void run() {
		while(true) {
			synchronized (s) {
				if(i%2==0) {
					s.name="张三";
					s.age=20;
				}else {
					s.name="李四";
					s.age=22;
				}
				i++;
			}
		}
	}
}

public class getThread implements Runnable {

	private Student s;
	public getThread(Student s) {
		this.s=s;
	}
	@Override
	public void run() {
		while(true) {
			synchronized (s) {
			System.out.println(s.name+":"+s.age);
			}
		}	
	}
}
  • 运行结果
张三:20
张三:20
张三:20
张三:20
张三:20
张三:20
张三:20
张三:20
张三:20
张三:20
李四:22
李四:22
李四:22
李四:22
李四:22
李四:22
李四:22
李四:22
李四:22
李四:22
李四:22
李四:22
李四:22
  • 对于这次的运行结果我们可以看到,设置值的结果没有出现问题。但是在方案一中只解决了线程安全问题,可能会出现以下问题:消费者没有在生产者生产之后再消费,也没有保证生产者等消费者消费之后再生产,而是一直生产下去。
  • 解决方案二:使用等待唤醒机制:
public class Student {

	String name;
	int age;
	boolean flag=false;//用于表示值有没有被设置
}
public class setThread implements Runnable {

	private Student s;
	private int i=0;
	public setThread(Student s) {
		this.s=s;
	}
	@Override
	public void run() {
		while(true) {
			synchronized (s) {
				if(s.flag)//flag代表有没有数据,有数据就等待,等待消费消费
					try {
						s.wait();//调用wait方法等待
					} catch (InterruptedException e) {
						// TODO 自动生成的 catch 块
						e.printStackTrace();
					}
				if(i%2==0) {
					s.name="张三";
					s.age=20;
				}else {
					s.name="李四";
					s.age=22;
				}
				i++;
				
				// 当以上代码执行完成之后,就有了商品。此时修改标记
				s.flag=true;
				//唤醒消费者
				s.notify();
			}
		}
	}
}
public class getThread implements Runnable {

	private Student s;
	public getThread(Student s) {
		this.s=s;
	}
	@Override
	public void run() {
		while(true) {
			synchronized (s) {
				if(!s.flag)//当没有商品时,就等待生产者
					try {
						s.wait();
					} catch (InterruptedException e) {
						// TODO 自动生成的 catch 块
						e.printStackTrace();
					}
				System.out.println(s.name+":"+s.age);
				
				//上述代码完成后就说明消费者已经消费量商品,将标记修改为false
				s.flag=false;
				//唤醒生产者线程生产
				s.notify();
			}
		}	
	}
}
  • 运行结果
张三:20
李四:22
张三:20
李四:22
张三:20
李四:22
张三:20
李四:22
张三:20
李四:22
张三:20
李四:22
张三:20
李四:22
  • 从运行的结果中我们可以看出,此时生产者设置一次值,消费者取一次值,不存在消费者一次取多次,也不存在生产者生产多次。wait()方法执行,就会立即释放锁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值