wait()和notify()是什么
虽然我们一般在多线程中使用wait和notify的方法,但其实它们是不属于Thread类的,他们是Object类中的方法,我们先来看一下API中的解释:
1、notify():唤醒在此对象监视器上等待的单个线程。
2、notifyAll():唤醒在此对象监视器上等待的所有线程。
3、wait():在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
这里我们可以知道wait和notify的功能分别是等待和唤醒,但这样的解释似乎有些笼统,我再具体解释一下:
wait()和notify()属于Object基础类,而Object类是所有类的父类,于是所有类都有这两种方法。每个对象都有自己的锁,且对每个对象来说,锁是唯一的。那么这个锁有什么作用呢?在多线程中,线程之间会竞争cpu资源,这会影响到线程的安全,所以这里我们要考虑到线程的同步问题,实现同步的方法便是对竞争性的资源加锁,加锁通过synchronized关键字实现。synchroniz(obj){ //想要加锁的内容 }。只有当线程获得了obj对象的锁,才能执行synchronize修饰的代码,因为锁的唯一性,在同一时刻只能有一个线程可以得到该对象的锁,这样就可以避免线程安全问题了。wait()和notify()是操作锁的两种方法,假定我们用obj对象给线程加锁(对象可以任取,但在多线程的同步时,synchronize修饰代码块时都应该去竞争同一对象的锁,不然就没有效果了,这点切记),执行obj.wait()可以使得当前对象失去obj的锁,让出资源的使用权,进入等待状态,暂停当前代码的执行,此时其他已被唤醒的线程中便有一个线程可以获得该资源(当有多个线程的时候,谁获得资源是不一定的)。执行obj.notify()方法可以唤醒在等待队列中的一个线程(没有优先级区分),使得此线程有机会去竞争这个锁。同理notifyAll()可以唤醒所有在等待队列中的线程。
生产者消费者模式
生产者消费者模式可以很好地解释和实现wait()和notify()的作用。顾名思义,生产者的作用就是生产物资,消费者的作用就是对物资进行消费,比如:自然界的植物光合作用为动物提供食物,人类社会的麦当劳的点单与处理订单等例子都可以诠释生产者消费者模式。我们这里拿一个最简单的例子来为大家解释,假设有一间仓库,生产者为仓库内添加物质,而消费者从仓库中取出物资,这里有三种情况:1、仓库为空,消费者暂停工作,等待生产者为仓库内添加物资。2、仓库满了,生产者暂停工作,等待消费者把物资从仓库中搬走。3、仓库内有物资,消费者和生产者同时工作。
wait()和notify()协调生产者和消费者工作的编程思路
我们怎样通过java程序实现呢?生产者和消费者是两个独立的线程,执行着各自的run方法。在上述第一种情况下,消费者执行obj.notify()方法,通知生产者开始工作了,再执行obj.wait()方法,让自己进入等待状态;第二种情况下,生产者执行obj.notify()方法,告诉消费者要开始工作了,再执行obj.wait()方法,让自己进入等待状态;第三种情况下,两者线程都处于运行状态,进行着生产和消费。
代码实现
为了更好地观察现象,我采取的程序设计思路是:创建生产者类和消费者类,让他们分别继承线程,改写run方法,主函数内创建线程对象并启动。生产者每次生产100的物资,消费者每次消费50的物资,初始仓库内物资为1000,达到2000为满。物资达到2000时生产者停止工作,唤醒消费者工作,待物资减到0,则消费者暂停工作,唤醒生产者开始工作,周而复始。现象就是物资的量在0~2000之间往复变化。
1、main方法
public class ProducerConsumer {
public int weight=1000; //仓库货物重量
public static void main(String[] args){
ProducerConsumer move= new ProducerConsumer();
Object warehouse = new Object();//定义仓库对象,之后为仓库加锁
Producer pro = new Producer();//生产者
Consumer con = new Consumer();//消费者
pro.setMove(move);
pro.setwarehouse(warehouse);
con.setMove(move);
con.setwarehouse(warehouse);
con.start();
pro.start();
}
2、生产者类
public class Producer extends Thread {
private Object warehouse;
private ProducerConsumer move;
public synchronized void run() {
while(true)
{
PutIn(warehouse);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private void PutIn(Object warehouse) {
// TODO Auto-generated method stub
synchronized (warehouse) {
if(move.weight<1900)
{
move.weight+=100;
System.out.println("仓库增加物资:"+100);
System.out.println("仓库现有物资:"+move.weight);
}
else if(move.weight>=1900&&move.weight<2000)
{
System.out.println("仓库增加物资:"+(2000-move.weight));
move.weight=2000;
System.out.println("仓库现有物资:"+move.weight);
try {
warehouse.notify();
warehouse.wait();
System.out.println("生产者被唤醒,消费者等待");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public void setMove(ProducerConsumer move) {
// TODO Auto-generated method stub
this.move= move;
}
public void setwarehouse(Object warehouse) {
// TODO Auto-generated method stub
this.warehouse=warehouse;
}
}
3、消费者类
public class Consumer extends Thread {
private Object warehouse;
private ProducerConsumer move;
public synchronized void run(){
while(true)
{
outPut(warehouse);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void outPut(Object warehouse){
synchronized (warehouse) {
if(move.weight>50)
{
move.weight-=50;
System.out.println("仓库减少物资:"+50);
System.out.println("仓库现有物资:"+move.weight);
}
else if(move.weight>=0&&move.weight<=50)
{
System.out.println("仓库减少物资:"+move.weight);
move.weight=0;
System.out.println("仓库现有物资:"+move.weight);
try {
warehouse.notify();
warehouse.wait();
System.out.println("消费者被唤醒,生产者等待");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public void setMove(ProducerConsumer move) {
// TODO Auto-generated method stub
this.move=move;
}
public void setwarehouse(Object warehouse) {
// TODO Auto-generated method stub
this.warehouse=warehouse;
}
}
程序运行结果部分截图: