#Java中线程间通信的方法
有这么几个方法,它们就定义在大家都非常熟悉的Object类中,但是大家却从来没有调用过,并且也不知道是做什么的,今天就由我带着你们熟悉一下下面的这三个方法。它们都是定义在Object类中的final方法,并且只能在synchronized上下文中调用。

  • wait()方法使当前线程进入休眠,直到另一个线程进入同一个监视器并调用nofity()方法。
  • nofity() 方法唤醒同一监视器内调用wait() 方法的线程,就是上面那位。
  • nofityAll()方法是nofify方法的升级版,会唤醒同一监视器内调用wait()方法的所有线程,但是由于这些线程都是同步线程,所以这些线程会以非并发的方式逐个被唤醒并拿到锁获得执行的机会。

生产者与消费者的场景

如果你现在还不明白,没关系,下面我会用一个“生产者与消费者"的场景来说明。首先,我会以不正确的方式来实现生产者与消费者。出现问题后,我再用wait()方法和notify() 方法来解决问题,相信大家一看就会明白。

关于生产者与消费者

生产者与消费者是一个经典的队列问题:生产者生产只有在所有产品都消费完了以后才进行生产,消费者只有在有产品的情况下才能消费,通俗来讲就是,生产一个,消费一个,再生产一个,再消费一个。。。。。。不能连续生产或连续消费。

##不正确的方式
该例子包含4个类,Queue是队列,放置生产者生产的产品,Producer和Consumer分别是生产者类和消费者类,Program类是包含main方法的类。
首先是Queue类,该类包含get()和put()这两个同步方法

public class Queue {
	int n;

	synchronized int get() {
		System.out.println("Got:" + n);
		return n;
	}

	synchronized void put(int n) {
		this.n = n;
		System.out.println("Put:" + n);
	}
}

接下来分别是Producer类和Consumer类,这两个类都实现了Runnable接口并覆写了run()方法。

public class Producer implements Runnable {
	Queue q;

	public Producer(Queue q) {
		this.q = q;
		new Thread(this, "Producer").start();
	}

	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			q.put(i);
		}
	}
}
public class Consumer implements Runnable {
	Queue q;

	public Consumer(Queue q) {
		this.q = q;
		new Thread(this, "Consumer").start();
	}

	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			q.get();
		}
	}
}

下面是main()方法

public class Program {

	public static void main(String[] args) {
		Queue q = new Queue();
		new Producer(q);
		new Consumer(q);
	}
}

尽管Queue类中的get()和put()方法都是同步的,但是并不能阻止生产者连续生产,也不能阻止消费者连续消费,这就导致输出的结果是错误的

Put:0
Put:1
Put:2
Put:3
Got:3
Got:3
Got:3
Got:3
Got:3
Put:4

使用wait()方法和notify()方法改造队列

接下来我们使用wait()方法和notify()方法来改造Queue类,代码如下

public class Queue {
	int n;
	boolean valueSet = false;

	synchronized int get() {
		while (!valueSet) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("Got:" + n);
		valueSet = false;
		notify();
		return n;
	}

	synchronized void put(int n) {
		while (valueSet) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.n = n;
		valueSet = true;
		System.out.println("Put:" + n);
		notify();
	}
}

改造后的队列的get()方法能够防止连续消费,put()方法能够防止连续生产,运行程序后的输出如下:

Put:0
Got:0
Put:1
Got:1
Put:2
Got:2
Put:3
Got:3
Put:4
Got:4

大家可以好好体会一下wait()和notify()方法,以及生产者和消费者