# 线程间通信
[TOC]
在我们之前的例子中,我们存在一个问题。当我们的账户余额不够了怎么办?我们就需要等待存入足够的钱后才能去处理。接下来,我们采用一个更直观更经典的一个例子说明
## 经典案例-生产者与消费者
![](https://img.kancloud.cn/36/17/3617a8a3da5d53d93e181689aa7fdc62_651x327.png)
~~~java
public class Queue {
private int n;
public synchronized int getN() {
System.out.println("消费:" + n);
return n;
}
public synchronized void setN(int n) {
System.out.println("生产:" + n );
this.n = n;
}
}
~~~
~~~java
public class Producer implements Runnable{
//共享queue类
Queue queue;
public Producer(Queue queue) {
this.queue = queue;
}
@Override
public void run() {
int i = 0;
while(true) {
i++;
//对queue类中的n进行赋值
queue.setN(i);
//模拟实际生产中线程被打断
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
~~~
~~~java
public class Consumer implements Runnable{
Queue queue;
public Consumer(Queue queue) {
this.queue = queue;
}
@Override
public void run() {
while(true) {
queue.getN();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
~~~
~~~java
public class PCTest {
public static void main(String[] args) {
Queue queue = new Queue();
Producer pd = new Producer(queue);
Consumer cs = new Consumer(queue);
Thread threadP = new Thread(pd);
Thread threadC = new Thread(cs);
threadP.start();
threadC.start();
}
}
~~~
本案例实现了一个基础的线程交互模型,但是通过执行结果,可以发现程序中存在两个问题。
1. **数据错位**
如果没有采用同步的话,生产者线程刚刚生产了数据,但是消费者线程取出的数据却不是刚刚生产出来的数据
2. **重复操作**
生产者生产了若干的数据,消费者才开始取出数据;或者是消费者去完一个数据后又接着取出数据。
## Object线程等待与唤醒
重复操作问题的解决,需要引入线程的等待与唤醒机制,而这一机制我们可以通过Object类完成。
>[info]Object类中定义有线程的等待与唤醒支持。我们主要要掌握Object类中的`wait()`,`notify()`,`notifyAll()`三个方法。
| 方法 | 说明 |
| :---: | :---: |
| public final void wait() throws InterruputedException | 线程的等待 |
| public final void wait(long timeout) throws InterruputedException | 设置线程等待毫秒数 |
| public final void wait(long timeout,int nanos) throws InterruputedException | 设置线程等待毫秒数和纳秒数 |
| public final void notify() | 唤醒某一个等待线程,使其结束等待 |
| public final void notifyAll() | 唤醒全部等待线程,使它们结束等待 |
**接下来,修改一下我们的程序吧**
~~~java
public class Queue {
private int n;
boolean flag = false;
public synchronized int getN() {
if(!flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("消费:" + n);
flag = false;//消费完毕,容器中没有数据
return n;
}
public synchronized void setN(int n) {
if(flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("生产:" + n );
this.n = n;
flag = true;//生产完毕,容器中已经有数据
}
}
~~~
在我们修改完这样的程序后,我们会发现我们的程序有可能会进入一个死锁状态。因为我们在代码中没有明确的唤醒处于等待状态的线程。
~~~java
public class Queue {
private int n;
boolean flag = false;
public synchronized int getN() {
if(!flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("消费:" + n);
flag = false;//消费完毕,容器中没有数据
notifyAll();
return n;
}
public synchronized void setN(int n) {
if(flag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("生产:" + n );
this.n = n;
flag = true;//生产完毕,容器中已经有数据
notifyAll();
}
}
~~~