1.概述
对于多线程来说,生产者—消费者模型是不可避免的问题。“宏观”来说生产者—消费者就是一边生产,一边消费,生产和消费同步。实际上是并发执行,一个线程执行一段时间。生产者生产完成后放入存储缓冲区,消费者从缓冲区拿走产品。那么问题来了:
- 同时去竞争缓冲区资源数据怎么保持一致?
- 消费者如何知道缓冲区有没有资源?
- 生产者如何知道缓冲区资源有没有满?
对于问题1,Java的synchronized关键字可以实现线程同步问题。
对于问题2、3就需要使用wait()、notify(),等待与唤醒。
2.wait()、notify()作用
wait()的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁。直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,当前线程被唤醒(进入“就绪状态”)。
注意:wait()、notify()、notifyAll()方法只能在synchronized语句块中使用,这三个方法都是Object的方法,调用的对象是锁对象。 例如:
synchronized(obj){
obj.wait();
}
wait()方法会释放锁,sleep()方法不释放锁。
3.例子
有一个仓库类entrepot,用list来表示缓冲区(list的长度为1)
class entrepot{
public List<Integer> res;
public entrepot() {
// TODO Auto-generated constructor stub
res=new ArrayList<>();
}
}
一个生产者线程,负责向list增加数据
class producer extends Thread{
public entrepot entrepot;
public producer(entrepot entrepot) {
// TODO Auto-generated constructor stub
this.entrepot=entrepot;
}
@Override
public void run() {
// TODO Auto-generated method stub
synchronized (entrepot) {
while(true) {
if(entrepot.res.size()>0) {
try {
entrepot.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int a=(int)(Math.random()*100);
entrepot.res.add(a);
System.out.println("生产:"+a);
entrepot.notifyAll();
}
}
}
}
一个消费者线程,负责删除list的数据
class consume extends Thread{
public entrepot entrepot;
public consume(entrepot entrepot) {
// TODO Auto-generated constructor stub
this.entrepot=entrepot;
}
@Override
public void run() {
// TODO Auto-generated method stub
synchronized (entrepot) {
while(true) {
if (entrepot.res.size()<1) {
try {
entrepot.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("消费:"+entrepot.res.remove(0));
entrepot.notifyAll();
}
}
}
}
当list的长度为1时,生产者等待,并释放仓库资源,当list长度为0时,消费者等待,并释放仓库资源。生产者生成完成之后就唤醒通知所有的进程,消费者完成后唤醒所有进程。
public class thread3 {
public static void main(String[] args) {
// TODO Auto-generated method stub
entrepot entrepot=new entrepot();
producer producer=new producer(entrepot);
consume consume=new consume(entrepot);
producer.start();
consume.start();
}
}
运行结果: