Java对象中隐式管程的应用是很强大的,但是你可以通过进程间通信达到更微妙的境界。这在Java中是尤为简单的。

多线程通过把任务分成离散的和合乎逻辑的单元代替了事件循环程序。线程还有第二优点:它远离了轮询。轮询通常由重复监测条件的循环实现。一旦条件成立,就要采取适当的行动。这浪费了CPU时间。

举例来说,考虑经典的序列问题,当一个线程正在产生数据而另一个程序正在消费它。为使问题变得更有趣,假设数据产生器必须等待消费者完成工作才能产生新的数据。

在轮询系统,消费者在等待生产者产生数据时会浪费很多CPU周期。一旦生产者完成工作,它将启动轮询,浪费更多的CPU时间等待消费者的工作结束,如此下去。很明显,这种情形不受欢迎。

为避免轮询,Java包含了通过wait( ),notify( )和notifyAll( )方法实现的一个进程间通信机制。这些方法在对象中是用final方法实现的,所以所有的类都含有它们。这三个方法仅在synchronized方法中才能被调用。

尽管这些方法从计算机科学远景方向上来说具有概念的高度先进性,实际中用起来是很简单的:
wait( ) 告知被调用的线程放弃管程进入睡眠直到其他线程进入相同管程并且调用notify( )。
notify( ) 恢复相同对象中第一个调用 wait( ) 的线程。
notifyAll( ) 恢复相同对象中所有调用 wait( ) 的线程。
具有最高优先级的线程最先运行。

这些方法在Object中被声明,如下所示:

final void wait( ) throws InterruptedException
final void notify( )
final void notifyAll( )

wait( )存在的另外的形式允许你定义等待时间。

下面的例子程序错误的实行了一个简单生产者/消费者的问题。它由四个类组成:Q,设法获得同步的序列;Producer,产生排队的线程对象;Consumer,消费序列的线程对象;以及PC,创建单个Q,Producer,和Consumer的小类。

// An incorrect implementation of a producer and consumer.
class Q { 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); }
}
class Producer implements Runnable { Q q; Producer(Q q) { this.q = q; new Thread(this, "Producer").start(); } public void run() { int i = 0; while(true) { q.put(i++); } }
}
class Consumer implements Runnable { Q q; Consumer(Q q) { this.q = q; new Thread(this, "Consumer").start(); } public void run() { while(true) { q.get(); } }
}
class PC { public static void main(String args[]) { Q q = new Q(); new Producer(q); new Consumer(q); System.out.println("Press Control-C to stop."); }
} 

尽管Q类中的put( )和get( )方法是同步的,没有东西阻止生产者超越消费者,也没有东西阻止消费者消费同样的序列两次。这样,你就得到下面的错误输出(输出将随处理器速度和装载的任务而改变):

Put: 1
Got: 1
Got: 1
Got: 1
Got: 1
Got: 1
Put: 2
Put: 3
Put: 4
Put: 5
Put: 6
Put: 7
Got: 7

生产者生成1后,消费者依次获得同样的1五次。生产者在继续生成2到7,消费者没有机会获得它们。

用Java正确的编写该程序是用wait( )和notify( )来对两个方向进行标志,如下所示:

// A correct implementation of a producer and consumer.
class Q { int n; boolean valueSet = false; synchronized int get() { if(!valueSet) try { wait(); } catch(InterruptedException e) { System.out.println("InterruptedException caught"); } System.out.println("Got: " + n); valueSet = false; notify(); return n; } synchronized void put(int n) { if(valueSet) try { wait(); } catch(InterruptedException e) { System.out.println("InterruptedException caught"); } this.n = n; valueSet = true; System.out.println("Put: " + n); notify(); }
}
class Producer implements Runnable { Q q; Producer(Q q) { this.q = q; new Thread(this, "Producer").start(); } public void run() { int i = 0; while(true) { q.put(i++); } }
}
class Consumer implements Runnable { Q q; Consumer(Q q) { this.q = q; new Thread(this, "Consumer").start(); } public void run() { while(true) { q.get(); } }
}
class PCFixed { public static void main(String args[]) { Q q = new Q(); new Producer(q); new Consumer(q); System.out.println("Press Control-C to stop."); }
}

内部get( ), wait( )被调用。这使执行挂起直到Producer 告知数据已经预备好。这时,内部get( ) 被恢复执行。获取数据后,get( )调用notify( )。这告诉Producer可以向序列中输入更多数据。

下面是该程序的输出,它清楚的显示了同步行为:

Put: 1
Got: 1
Put: 2
Got: 2
Put: 3
Got: 3
Put: 4
Got: 4
Put: 5
Got: 5

在put( )内,wait( )挂起执行直到Consumer取走了序列中的项目。当执行再继续,下一个数据项目被放入序列,notify( )被调用,这通知Consumer它应该移走该数据。

开课吧Java课堂:线程间是如何实现通信相关推荐

  1. Java 如何线程间通信,面试被问哭。。。

    Java 如何线程间通信,曾经小编面试被问哭的一道题.. 正常情况下,每个子线程完成各自的任务就可以结束了.不过有的时候,我们希望多个线程协同工作来完成某个任务,这时就涉及到了线程间通信了. 本文涉及 ...

  2. 开课吧Java课堂:什么是线程优先级?

    Java给每个线程安排优先级以决定与其他线程比较时该如何对待该线程.线程优先级是详细说明线程间优先关系的整数. 作为绝对值,优先级是毫无意义的:当只有一个线程时,优先级高的线程并不比优先权低的线程运行 ...

  3. Java并发——线程间通信与同步技术

    传统的线程间通信与同步技术为Object上的wait().notify().notifyAll()等方法,Java在显示锁上增加了Condition对象,该对象也可以实现线程间通信与同步.本文会介绍有 ...

  4. java实现线程间通信的四种方式

    synchronized同步 public class MyObject { synchronized public void methodA() { //do something.... } syn ...

  5. java基础—线程间的通讯 生产者与消费者

    线程间的的通讯  生产者与消费者 public class TestDemos3 {public static void main(String[] args){Res r = new Res();I ...

  6. 开课吧Java课堂:多线程如何同步?消息如何传递?

    同步性 因为多线程在你的程序中引入了一个异步行为,所以在你需要的时候必须有加强同步性的方法.举例来说,如果你希望两个线程相互通信并共享一个复杂的数据结构,例如链表序列,你需要某些方法来确保它们没有相互 ...

  7. 开课吧Java课堂:什么是主线程?如何去运用?

    当Java程序启动时,一个线程立刻运行,该线程通常叫做程序的主线程,因为它是程序开始时就执行的.主线程的重要性体现在两方面: · 它是产生其他子线程的线程 · 通常它必须最后完成执行,因为它执行各种关 ...

  8. 开课吧Java课堂:Transient和volatile修饰符如何运用

    Java定义了两类有趣的修饰符:transient和volatile,这些修饰符用来处理特殊的情况.如果用transient声明一个实例变量,当对象存储时,它的值不需要维持.例如: class T { ...

  9. 开课吧Java课堂:如何创建多线程

    大多数情况,通过实例化一个Thread对象来创建一个线程.Java定义了两种方式: · 实现Runnable 接口. · 可以继承Thread类. 为什么Java有两种创建子线程的方法?所有的问题都归 ...

最新文章

  1. Hibernate Annotation _List/Map
  2. python socket 书籍_Python学习之路——socket
  3. HTML5 高级系列:web Storage 学前端开发要先看这个
  4. 微软模拟飞行10厦门航空涂装_《微软飞行模拟器》多人游戏模式演示:可组队飞行...
  5. 200多个js技巧代码(五)
  6. [js] 渲染树构建、布局及绘制
  7. 免费证书https://lamp.sh/ssl.html
  8. 为什么京东买手机不支持白条?
  9. BZOJ1123: [POI2008]BLO
  10. 《软件工程》第6章体系结构设计
  11. 免费抖音视频解析网站_抖音规则解析:抖音视频为什么能火?推荐规则是什么?【揭秘】...
  12. Extjs Design 可视化开发工具
  13. Xilinx差分输入时钟100Ω终端电阻设置
  14. 音视频 开发技术,让智能家居更智能!
  15. java 获取docker ip_docker容器内部获取宿主机ip地址方法以及报错解决
  16. 华为鸿蒙国内厂商适配,华为再放大招!鸿蒙系统将适配高通/联发科手机:获国产厂商力挺...
  17. 机器学习之深度学习简介
  18. 计算机z,出国留学_计算机词汇(R-Z)_沪江英语
  19. springmvc-kuang
  20. 数学基础知识积累——傅里叶分析

热门文章

  1. [转载] Python 学习笔记 迭代器和生成器
  2. [转载] Java获取嵌套的json串里的返回结果
  3. Ajax搜索提示功能
  4. Thread 相关函数和属性
  5. Day4:python之文件操作、函数初识(2)
  6. BigDecimal 小数 浮点数 精度 财务计算
  7. 【逆向知识】VS程序反汇编找main函数
  8. 从C#到Nodejs,从windowns到mac
  9. 在 ASP.NET MVC 中创建自定义 HtmlHelper
  10. python---之hasattr()