全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

Java 存储模型和共享对象详解

Java 存储模型和共享对象详解

很多程序员对一个共享变量初始化要注意可见性和安全发布(安全地构建一个对象,并其他线程能正确访问)等问题不是很理解,认为Java是一个屏蔽内存细节的平台,连对象回收都不需要关心,因此谈到可见性和安全发布大多不知所云。其实关键在于对Java存储模型,可见性和安全发布的问题是起源于Java的存储结构。

Java存储模型原理

有很多书和文章都讲解过Java存储模型,其中一个图很清晰地说明了其存储结构:

由上图可知, jvm系统中存在一个主内存(Main Memory或Java Heap Memory),Java中所有变量都储存在主存中,对于所有线程都是共享的。 每条线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。

这个存储模型很像我们常用的缓存与数据库的关系,因此由此可以推断JVM如此设计应该是为了提升性能,提高多线程的并发能力,并减少线程之间的影响。

Java存储模型潜在的问题

一谈到缓存, 我们立马想到会有缓存不一致性问题,就是说当有缓存与数据库不一致的时候,就需要有相应的机制去同步数据。同理,Java存储模型也有这个问题,当一个线程在自己工作内存里初始化一个变量,当还没来得及同步到主存里时,如果有其他线程来访问它,就会出现不可预知的问题。另外,JVM在底层设计上,对与那些没有同步到主存里的变量,可能会以不一样的操作顺序来执行指令,举个实际的例子:

public class PossibleReordering { 
  static int x = 0, y = 0; 
  static int a = 0, b = 0; 
  public static void main(String[] args) 
      throws InterruptedException { 
    Thread one = new Thread(new Runnable() { 
      public void run() { 
        a = 1; 
        x = b; 
      } 
    }); 
    Thread other = new Thread(new Runnable() { 
      public void run() { 
        b = 1; 
        y = a; 
      } 
    }); 
    one.start(); other.start(); 
    one.join();  other.join(); 
    System.out.println("( "+ x + "," + y + ")"); 
  } 
} 

由于,变量x,y,a,b没有安全发布,导致会不以规定的操作顺序来执行这次四次赋值操作,有可能出现以下顺序:

出现这个问题也可以理解,因为既然这些对象不可见,也就是说本应该隔离在各个线程的工作区内,那么对于有些无关顺序的指令,打乱顺序执行在JVM看来也是可行的。

因此,总结起来,会有以下两种潜在问题:

  1. 缓存不一致性
  2. 重排序执行

解决Java存储模型潜在的问题

为了能让开发人员安全正确地在Java存储模型上编程,JVM提供了一个happens-before原则,有人整理得非常好,我摘抄如下:

  1. 在程序顺序中, 线程中的每一个操作, 发生在当前操作后面将要出现的每一个操作之前.
  2. 对象监视器的解锁发生在等待获取对象锁的线程之前.
  3. 对volitile关键字修饰的变量写入操作, 发生在对该变量的读取之前.
  4. 对一个线程的 Thread.start() 调用 发生在启动的线程中的所有操作之前.
  5. 线程中的所有操作 发生在从这个线程的 Thread.join()成功返回的所有其他线程之前.

有了原则还不够,Java提供了以下工具和方法来保证变量的可见性和安全发布:

  1. 使用 synchronized来同步变量初始化。此方式会立马把工作内存中的变量同步到主内存中
  2. 使用 volatile关键字来标示变量。此方式会直接把变量存在主存中而不是工作内存中
  3. final变量。常量内也是存于主存中

另外,一定要明确只有共享变量才会有以上那些问题,如果变量只是这个线程自己使用,就不用担心那么多问题了
搞清楚Java存储模型后,再来看共享对象可见性和安全发布的问题就较为容易了

共享对象的可见性

当对象在从工作内存同步到主内存之前,那么它就是不可见的。若有其他线程在存取不可见对象就会引发可见性问题,看下面一个例子:

public class NoVisibility { 
  private static boolean ready; 
  private static int number; 
  private static class ReaderThread extends Thread { 
    public void run() { 
      while (!ready) 
        Thread.yield(); 
      System.out.println(number); 
    } 
  } 
  public static void main(String[] args) { 
    new ReaderThread().start(); 
    number = 42; 
    ready = true; 
  } 
} 

按照正常逻辑,应该会输出42,但其实际结果会非常奇怪,可能会永远没有输出(因为ready为false),可能会输出0(因为重排序问题导致ready=true先执行)。再举一个更为常见的例子,大家都喜欢用只有set和get方法的pojo来设计领域模型,如下所示:

@NotThreadSafe 
public class MutableInteger { 
  private int value; 
  public int get() { return value; } 
  public void set(int value) { this.value = value; } 
} 

但是,当有多个线程同时来存取某一个对象时,可能就会有类似的可见性问题。
为了保证变量的可见性,一般可以用锁、 synchronized关键字、 volatile关键字或直接设置为final

共享变量发布

共享变量发布和我们常说的发布程序类似,就是说让本属于内部的一个变量变为一个可以被外部访问的变量。发布方式分为以下几种:

  • 将对象引用存储到公共静态域
  • 初始化一个可以被外部访问的对象
  • 将对象引用存储到一个集合里

安全发布和保证可见性的方法类似,就是要同步发布动作,并使发布后的对象可见。

线程安全

其实当我们把这些变量封闭在本线程内访问,就可以从根本上避免以上问题,现实中存在很多例子通过线程封闭来安全使用本不是线程安全的对象,比如:

  1. swing的可视化组件和数据模型对象并不是线程安全的,它通过将它们限制到swing的事件分发线程中,实现线程安全
  2. JDBC Connection对象没有要求为线程安全,但JDBC的存取模式决定了一个Connection只会同时被一个线程使用
  3. ThreadLocal把变量限制在本线程中共享

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!


# Java  # 存储模型和共享对象  # 存储模型和共享对象分析  # 存储模型与共享对象  # Java并发编程之对象的共享  # Java多线程编程之访问共享对象和数据的方法  # java 域对象共享数据的实现  # 见性  # 会有  # 就会  # 性问题  # 这个问题  # 发生在  # 自己的  # 的是  # 都是  # 是一个  # 是在  # 都有  # 也有  # 还没  # 都不  # 地说  # 多个  # 有可能  # 有很多  # 那么多 


相关文章: 教学网站制作软件,学习*后期制作的网站有哪些?  TestNG的testng.xml配置文件怎么写  Swift中swift中的switch 语句  香港服务器部署网站为何提示未备案?  ,南京靠谱的征婚网站?  香港服务器选型指南:免备案配置与高效建站方案解析  宝塔建站教程:一键部署配置流程与SEO优化实战指南  高防服务器租用如何选择配置与防御等级?  如何选择CMS系统实现快速建站与SEO优化?  如何在Mac上搭建Golang开发环境_使用Homebrew安装和管理Go版本  建站之星3.0如何解决常见操作问题?  深圳网站制作公司好吗,在深圳找工作哪个网站最好啊?  免费制作统计图的网站有哪些,如何看待现如今年轻人买房难的情况?  如何安全更换建站之星模板并保留数据?  C++时间戳转换成日期时间的步骤和示例代码  Python如何创建带属性的XML节点  如何通过虚拟主机空间快速建站?  制作企业网站建设方案,怎样建设一个公司网站?  建站之星各版本价格是多少?  唐山网站制作公司有哪些,唐山找工作哪个网站最靠谱?  免费网站制作模板下载,除了易企秀之外还有什么H5平台可以制作H5长页面,最好是免费的?  如何用免费手机建站系统零基础打造专业网站?  大连网站制作费用,大连新青年网站,五年四班里的视频怎样下载啊?  如何在阿里云虚拟机上搭建网站?步骤解析与避坑指南  c# 在ASP.NET Core中管理和取消后台任务  建站之星如何防范黑客攻击与数据泄露?  如何快速使用云服务器搭建个人网站?  建站主机是否属于云主机类型?  关于BootStrap modal 在IOS9中不能弹出的解决方法(IOS 9 bootstrap modal ios 9 noticework)  制作公司内部网站有哪些,内网如何建网站?  韩国服务器如何优化跨境访问实现高效连接?  天河区网站制作公司,广州天河区如何办理身份证?需要什么资料有预约的网站吗?  官网自助建站系统:SEO优化+多语言支持,快速搭建专业网站  详解jQuery中基本的动画方法  制作充值网站的软件,做人力招聘为什么要自己交端口钱?  专业型网站制作公司有哪些,我设计专业的,谁给推荐几个设计师兼职类的网站?  如何选择建站程序?包含哪些必备功能与类型?  制作销售网站教学视频,销售网站有哪些?  黑客如何利用漏洞与弱口令入侵网站服务器?  如何快速生成高效建站系统源代码?  网站视频怎么制作,哪个网站可以免费收看好莱坞经典大片?  百度网页制作网站有哪些,谁能告诉我百度网站是怎么联系?  怎么制作网站设计模板图片,有电商商品详情页面的免费模板素材网站推荐吗?  建站主机与服务器功能差异如何区分?  Android滚轮选择时间控件使用详解  宝华建站服务条款解析:五站合一功能与SEO优化设置指南  盘锦网站制作公司,盘锦大洼有多少5G网站?  如何在Golang中使用encoding/gob序列化对象_存储和传输数据  英语简历制作免费网站推荐,如何将简历翻译成英文?  已有域名和空间,如何快速搭建网站? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。