🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 线程间通信 [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(); } } ~~~