wait/notify讲解

发表时间:2017-09-07 16:36:41 浏览量( 14 ) 留言数( 0 )

学习目标:

1、了解wati/notify的作用

2、能在实际编程中使用wait/notify


学习过程:

   Java.lang.Object 类提供了一套等待/通知的API ,wait()和notify()/notify All()方法组成。wait()方法等待某个条件成立,当这个条件成立时, notify() 和l noti fyAll()方法通知处于等待中的线程。这些方法是定义在Object的方法里面的,所以对象都又这些方法,但是注意些方法不是给线程类使用的。

   一般来说这些方法的使用都一定是写在锁里面,我们可以看一下这么这个例子:

先定义一个类:

public class Thread1 extends Thread {

	public Object object;

	public Thread1(Object object) {
		this.object = object;
	}

	public void run() {

		synchronized (object) {
				try {
					System.out.println(this.getName()+"在里面等待了。。。");
					object.wait();//程序运行到这里会停下来
					System.out.println(this.getName()+"获得object了。。。");
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		}
	}
}

在定义另一个企图获得Object的运行类:

public class NotifyThread extends Thread {
	public Object object;

	public NotifyThread(Object object) {
		this.object = object;
	}

	public void run() {
		synchronized (object) {
			System.out.println("我可以进来了。");
			object.notify();
			System.out.println("我释放了object");
		}

	}
}

写一个测试运行类:

public class RunNotify1 {
	public static void main(String[] args) throws InterruptedException {
		
		Object object=new Object();
		
		Thread1 thread1=new Thread1(object);

		NotifyThread notifyThread=new NotifyThread(object);
		
		thread1.start();
		Thread.sleep(2000);
		
		notifyThread.start();
		
	}
}

看一下运行结果:

attcontent/bf8e1f25-48d9-4807-bd41-01914e975d54.png    

程序在锁定并获得object对象后运行,执行到object.wait()方法的时候就会吧object锁释放,这时候另一个线程就可以获得object对象的锁了,而这个线程在执行object.notify()时会通知object的wait()的调用线程,但是要注意这个时候object还没有释放的,nofify是在退出同步块后才会释放锁的,而wait方法时会马上释放锁。


二、经典的生产者和消费者模式的讲解  

   生产者和消费者模式在生活当中随处可见,它描述的是协调与协作的关系。比如一个人正在准备食物(生产者),而另一个人正在吃(消费者),他们使用一个共用的桌子用于放置盘子和取走盘子,生产者准备食物,如果桌子上已经满了就等待,消费者(那个吃的)等待如果桌子空了的话。

    它的确是一种实用的设计模式,常用于编写多线程或并发代码。下面是它的一些优点:

    1、它简化的开发,你可以独立地或并发的编写消费者和生产者,它仅仅只需知道共享对象是谁

    2、生产者不需要知道谁是消费者或者有多少消费者,对消费者来说也是一样

    3、生产者和消费者可以以不同的速度执行

    4、分离的消费者和生产者在功能上能写出更简洁、可读、易维护的代码

通道类:

public class Channel {

	private char c;
	private volatile boolean isWrite = true;

	public synchronized void putChar(char c) {
		try {
			while (!isWrite) {
				wait();
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		isWrite = false;
		notifyAll();
		this.c = c;
	}

	public synchronized char getChar() {
		try {
			while (isWrite) {
				wait();
			}
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		isWrite = true;
		notifyAll();
		return c;
	}

}

生产者类:

public class ThreadPro extends Thread {

	public Channel channel;

	public ThreadPro(Channel channel) {
		this.channel = channel;
	}

	public void run() {
		synchronized (channel) {
			for (char c = 'a'; c <= 'z'; c++) {
				channel.putChar(c);
				System.out.println("生产端生产了:" + c);
			}
		}

	}

}

消费者类

public class ThreadConsumer extends Thread {
	public Channel channel;

	public ThreadConsumer(Channel channel) {
		this.channel = channel;
	}

	public void run() {
        synchronized (channel) {
        	char c=channel.getChar();
    		while(c!='z') {
    			System.out.println("消费端消费了:"+c);
    			c=channel.getChar();
    		}
		}
		

	}
}

运行类

public class Run1 {
	
	public static void main(String[] args) {
		
		Channel channel=new Channel();
		
		ThreadConsumer threadConsumer=new ThreadConsumer(channel);
		ThreadPro threadPro=new ThreadPro(channel);
		
		threadConsumer.start();
		threadPro.start();
		
	}

}



三、notify和notifyAll的代码差别

   如果有多个线程的object对象在wait时,notify会唤醒其中一个线程,而notifyAll还唤醒所有的,如果你自己的也确定时有多少个线程在wait,那么一般会使用notifyAll,如果多个线程在wait的时候,而你也写了一个notify,那么有可能导致有些线程会一直等待,占用系统的资源。

我们可以做一个实验。使用第一节的时候定义的线程,定义三个线程,和一个唤醒线程测试

NotifyThread里面的唤醒步骤我们先使用

object.notify();

一会再修改成为

object.notifyAll();

测试类如下:

public class Run2 {

public static void main(String[] args) throws InterruptedException {

Object object=new Object();

Thread1 thread1=new Thread1(object);

Thread1 thread2=new Thread1(object);

Thread1 thread3=new Thread1(object);

NotifyThread notifyThread=new NotifyThread(object);

thread1.start();

thread2.start();

thread3.start();

Thread.sleep(2000);

notifyThread.start();

}


}

输出如下:

attcontent/01a29793-fdc8-4a7a-bb1f-31a1011284a3.png

可以看到线程并没有结束。

改成NotifyAll,看看输出的结果

attcontent/ae00ed8b-994a-4af4-aff4-6d9eb55eba42.png

三个线程都可以得到通知,并且最后都能运行结束了。


四、wait参数

wait还可以添加时间参数,如果超过多长时间后就算没有notify也会继续往后面运行。

wait(long) 超过时间就不会等待了,程序继续运行下去,如下面所示,你可以自己做一下试验

object.wait(3000);