当前位置: 首页 > news >正文

Java线程通信的简介说明

转自:

线程通信

为了提高程序的运行效率,我们常常使用多线程进行开发程序,那么多线程之间有哪些方式可以进行通信呢?下文将一一到来,如下所示:

使用共享对象通信

使用共享变量,使每个线程中都可以访问此变量,如下所示:
线程A 中设置变量hasDataToProcess的值
线程B 中读取变量hasDataToProcess的值

如下例所示:

 
public class MyClass{
  protected boolean hasDataToProcess = false;
  public synchronized boolean hasDataToProcess(){
    return this.hasDataToProcess;
  }

  public synchronized void setHasDataToProcess(boolean hasData){
    this.hasDataToProcess = hasData;
  }
}

/*
线程A和线程B同时指向MyClass实例,
此时我们就可以获取其hasDataToProcess或设置setHasDataToProcess达到
hasDataToProcess信息共享的目的
*/

忙等待(Busy Wait)

线程B中一直循环等待“线程A中指定值”发生变化,当线程A修改指定值时,则线程B可收到线程A的指令
例:

protected MySignal sharedSignal = ...
while(!sharedSignal.hasDataToProcess()){
  //线程B处于等待状态
}

wait(),notify()和notifyAll()

在Java中有一个内部等待机制来允许线程在等待信号的时候变为非运行状态,如:
java.lang.Object类定义三个方法:wait(),notify(),notifyAll()都可实现这个等待机制

当一个线程一旦调用任意对象的wait()方法
 就会变为非运行状态
 直到另一个线程调用了同一个对象的notify()方法
------------------------------------------------------------------------------------------------------------
为了调用wait()或者notify(),线程必须先获得那个对象的锁,即:线程必须在同步块里调用wait()或者notify()

例:
MySingal使用了wait()和notify()的MyWaitNotify

public class MonitorObject{
}

public class MyWaitNotify{
  MonitorObject myMonitorObject = new MonitorObject();
  public void doWait(){
    synchronized(myMonitorObject){
      try{
        myMonitorObject.wait();
      } catch(InterruptedException e){...}
    }
  }

  public void doNotify(){
    synchronized(myMonitorObject){
    myMonitorObject.notify();
    }
  }
}

等待线程将调用doWait()
而唤醒线程将调用doNotify()
当一个线程调用一个对象的notify()方法
正在等待该对象的所有线程中将有一个线程被唤醒并允许执行
同时也提供了一个notifyAll()方法来唤醒正在等待一个给定对象的所有线程。

不管是等待线程还是唤醒线程都在必须在同步块里调用wait()和notify()
一个线程如果没有持有对象锁,将不能调用wait(),notify()或者notifyAll()
如果调用,会抛出IllegalMonitorStateException异常

丢失的信号(Missed Signals)

notify()和notifyAll()方法不会保存调用它们的方法
当这两个方法被调用时,有可能没有线程处于等待状态
通知信号过后便丢弃了
即:一个线程先于被通知线程调用wait()前调用了notify(),等待的线程将错过这个信号
不过,在某些情况下,这可能使等待线程永远在等待,不再醒来,因为线程错过了唤醒信号。 为了避免丢失信号,必须把它们保存在信号类里
在MyWaitNotify的例子中,通知信号应被存储在MyWaitNotify实例的一个成员变量里
例:MyWaitNotify的修改版本:

public class MyWaitNotify2{
  MonitorObject myMonitorObject = new MonitorObject();
  boolean wasSignalled = false;
  public void doWait(){
    synchronized(myMonitorObject){
      if(!wasSignalled){
        try{
          myMonitorObject.wait();
         } catch(InterruptedException e){...}
      }
      //clear signal and continue running.
      wasSignalled = false;
    }
  }
 
  public void doNotify(){
    synchronized(myMonitorObject){
      wasSignalled = true;
      myMonitorObject.notify();
    }
  }
}
/*
doNotify()方法在调用notify()前把wasSignalled变量设为true
同时doWait()方法在调用wait()前会检查wasSignalled变量
当没有信号在前一次doWait()调用和这次doWait()调用之间的时间段里被接收到,它将只调用wait()
*/

假唤醒

线程有可能在没有调用过notify()和notifyAll()的情况下醒来
这就是所谓的假唤醒(spurious wakeups) 如果在MyWaitNotify2的doWait()方法里发生了假唤醒,等待线程即使没有收到正确的信号,也能够执行后续的操作。这可能导致你的应用程序出现严重问题
为了防止假唤醒,保存信号的成员变量将在一个while循环里接受检查,而不是在if表达式里。这样的一个while循环叫做自旋锁
被唤醒的线程会自旋直到自旋锁(while循环)里的条件变为false
例:

public class MyWaitNotify3{
  MonitorObject myMonitorObject = new MonitorObject();
  boolean wasSignalled = false;
  public void doWait(){
    synchronized(myMonitorObject){
      while(!wasSignalled){
        try{
          myMonitorObject.wait();
         } catch(InterruptedException e){...}
      }
      //clear signal and continue running.
      wasSignalled = false;
    }
  }

  public void doNotify(){
    synchronized(myMonitorObject){
      wasSignalled = true;
      myMonitorObject.notify();
    }
  }
}

多个线程等待相同信号

当多个线程在等待,被notifyAll()唤醒,
但只有一个被允许继续执行,使用while循环也是个好方法。每次只有一个线程可以获得监视器对象锁
则只有一个线程可以退出wait()调用并清除wasSignalled标志(设为false)
一旦这个线程退出doWait()的同步块,其他线程退出wait()调用
并在while循环里检查wasSignalled变量值
这个标志已经被第一个唤醒的线程清除了,所以其余醒来的线程将回到等待状态,直到下次信号到来

不要在字符串常量或全局对象中调用wait()

public class MyWaitNotify{
  String myMonitorObject = "";
  boolean wasSignalled = false;
  public void doWait(){
    synchronized(myMonitorObject){
      while(!wasSignalled){
        try{
          myMonitorObject.wait();
         } catch(InterruptedException e){...}
      }
      //clear signal and continue running.
      wasSignalled = false;
    }
  }
 
  public void doNotify(){
    synchronized(myMonitorObject){
      wasSignalled = true;
      myMonitorObject.notify();
    }
  }
}

相关文章:

  • 对JavaBean的特点写法与实战心得详解
  • 【手写算法实现】 之 朴素贝叶斯 Naive Bayes 篇
  • Vue.js核心技术解析与uni-app跨平台实战开发学习笔记 第12章 Vue3.X新特性解析 12.12 响应式系统工具集的使用
  • java服务器端开发-servlet:202、Servlet执行过程介绍:get请求与post请求、编码相关等
  • yara 分析器
  • 数据结构(三) -- 栈
  • 神策数据发布融媒行业版,驱动媒体深度融合转型
  • 解决安装GDAL库报错问题(Windos)
  • 数据逻辑校验机制
  • Linux关于jar包的基本操作
  • 用什么软件可以提高视频批量剪辑的效率
  • 搜索替换 csv 文件中的文本
  • DBCO-PEG-Casein/Ovalbumin/Lectins点击化学DBCO偶联络蛋白/卵清蛋白/凝集素
  • 【JetPack Compose】声明式UI 、组合、重组
  • jquary样式操作、动画
  • .pyc 想到的一些问题
  • AngularJS指令开发(1)——参数详解
  • Apache Pulsar 2.1 重磅发布
  • C++类中的特殊成员函数
  • go语言学习初探(一)
  • HTTP中的ETag在移动客户端的应用
  • JavaScript工作原理(五):深入了解WebSockets,HTTP/2和SSE,以及如何选择
  • Java读取Properties文件的六种方法
  • Linux快速复制或删除大量小文件
  • Mac 鼠须管 Rime 输入法 安装五笔输入法 教程
  • Mysql5.6主从复制
  • Redis在Web项目中的应用与实践
  • Sequelize 中文文档 v4 - Getting started - 入门
  • ⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)
  • VuePress 静态网站生成
  • Vue全家桶实现一个Web App
  • Vue小说阅读器(仿追书神器)
  • 关于for循环的简单归纳
  • 开源SQL-on-Hadoop系统一览
  • 聊聊hikari连接池的leakDetectionThreshold
  • 听说你叫Java(二)–Servlet请求
  • d²y/dx²; 偏导数问题 请问f1 f2是什么意思
  • #{}和${}的区别是什么 -- java面试
  • ( 10 )MySQL中的外键
  • (30)数组元素和与数字和的绝对差
  • (安卓)跳转应用市场APP详情页的方式
  • (动手学习深度学习)第13章 计算机视觉---微调
  • (附源码)SSM环卫人员管理平台 计算机毕设36412
  • (论文阅读30/100)Convolutional Pose Machines
  • (终章)[图像识别]13.OpenCV案例 自定义训练集分类器物体检测
  • (转)大型网站的系统架构
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • **登录+JWT+异常处理+拦截器+ThreadLocal-开发思想与代码实现**
  • .[hudsonL@cock.li].mkp勒索加密数据库完美恢复---惜分飞
  • .NET CF命令行调试器MDbg入门(三) 进程控制
  • .NET Project Open Day(2011.11.13)
  • .Net多线程总结
  • @SentinelResource详解
  • [ C++ ] template 模板进阶 (特化,分离编译)
  • [ CTF ]【天格】战队WriteUp- 2022年第三届“网鼎杯”网络安全大赛(青龙组)