# 線程間通信
[TOC]
在我們之前的例子中,我們存在一個問題。當我們的賬戶余額不夠了怎么辦?我們就需要等待存入足夠的錢后才能去處理。接下來,我們采用一個更直觀更經典的一個例子說明
## 經典案例-生產者與消費者

~~~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();
}
}
~~~
- JAVA基礎
- JAVA開發準備
- JAVA介紹
- 開發JAVA的準備
- JAVA的運行原理
- JDK配置
- 我的第一個JAVA程序
- 類與對象
- 基礎語言要素
- 數據類型
- eclipse的安裝與使用
- 變量
- 直接量
- 運算符
- 流程控制
- 數組結構
- 面向對象
- 隱藏與封裝
- 深入構造器
- 類的繼承
- 多態
- 包裝類
- final修飾符
- 抽象類
- 接口
- 內部類
- 設計模式
- 單例模式
- 工廠模式
- 集合框架
- 集合排序
- 常用類學習
- 異常處理
- Java基礎測試
- 綜合案例一
- JAVA高級
- 泛型
- 多線程
- 線程的創建
- 線程的生命周期
- 線程同步
- 線程通信
- 輸入輸出流(I/O編程)
- File文件操作
- 字節流與字符流
- 數據庫
- 數據庫介紹
- 數據庫安裝
- SQL
- 表的基本操作
- 修改數據語句
- 數據檢索操作
- 多表數據操作
- 表結構設計
- 綜合應用