生产者负责生产数据,当每生成一个完整的数据后,消费者就将数据取走。
程序中会存在两类问题:
1、 数据不完整;
2、 数据的重复操作问题(重复设置或者重复取出)。
解决数据的同步问题和数据的重复操作问题思路:
- synchronized关键字。
- 解决重复设置和重复取出问题,就要增加等待与唤醒机制。参考Object类中提供的方法。
1. 普通: 等待、死等
public final void wait() throws InterruptedException
2. 普通:唤醒第一个等待的线程
public final void notify()
3. 普通:唤醒全部等待的线程,哪个优先级高,谁就有可能执行。
public final void notifyAll()
代码示例:
package cn.edu.www;
class DataProvider implements Runnable{
private Data data;
public DataProvider(Data data) {
this.data=data;
}
@Override
public void run() {
for(int x=0;x<50;x++) {
if (x%2==0) {
this.data.set("张三","姓张");
}else
{
this.data.set("李四","姓李");
}
}
}
}
class DataConsumer implements Runnable{
private Data data;
public DataConsumer(Data data){
this.data=data;
}
@Override
public void run() {
for(int x=0;x<50;x++) {
this.data.get();
}
}
}
class Data{
private String name;
private String note;
//flag=true表示允许生产,但是不允许消费者取走
//flag=false表示生产完毕,允许消费者取走,但是不能够生产
private boolean flag=false;
public synchronized void get() {
if(flag==false) {//已经生产,所以不允许重复生产
try {
super.wait();//等待执行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.name+"="+this.note);
this.flag=false;//表示生产过了,不允许再生产了
super.notify();//唤醒等待线程
}
public synchronized void set(String name,String note) {
if(this.flag==true) {
try {
super.wait();//等待执行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name=name;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.note=note;
this.flag=true;//继续生产
super.notify();//唤醒其他
}
}
public class TestDemo {
public static void main(String[] args) {
Data data=new Data();
new Thread(new DataProvider(data)).start();
new Thread(new DataConsumer(data)).start();
}
}
运行结果:
张三=姓张
李四=姓李
张三=姓张
李四=姓李
张三=姓张
李四=姓李
张三=姓张
李四=姓李
//重复很多次,太多了不写了。
面试题:请解释sleep()和wait()的区别?
1、 sleep()是Thread类中定义的方法,到了一定的时间后该休眠的线程可以自动唤醒;
2、 wait()是Object类中定义的方法,如果要想唤醒,必须使用notify()、和notifyAll()方法才可以唤醒。