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

陕西网站开发哪家好专门做折扣的网站

陕西网站开发哪家好,专门做折扣的网站,网站开发电脑设置,设计培训学校概述 Java内存模型的主要目标是定义程序中各个变量的访问规则#xff0c;即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。此处的变量是线程共享的#xff0c;存在竞争问题的。 Java内存模型规定了所有的变量都存储在主内存#xff0c;每条线程还有自己的工… 概述 Java内存模型的主要目标是定义程序中各个变量的访问规则即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。此处的变量是线程共享的存在竞争问题的。   Java内存模型规定了所有的变量都存储在主内存每条线程还有自己的工作内存中保存了被该线程使用到的变量的主内存副本拷贝线程对变量的所有操作读取、赋值等都必须在工作内存中进行而不能直接读写主内存中的变量。注这里说的工作内存或本地内存都是虚拟出来的实质上包括了寄存器、缓存或中间的存储器 不同的线程之间也无法直接访问对方工作内存中的变量线程间变量值的传递均需要通过主内存来完成三者关系的交互如图所示 如何交互 那么内存之间是如何交互的呢JMM定义了8中操作来完成虚拟机保证每一种操作都是原子性的。 Lock作用于主内存的变量它把一个变量标识为线程独占状态 Unlock作用于主内存的变量将一个处于锁定状态下的变量释放出来释放后的变量才可以被其他线程加锁。 Read作用于主内存的变量将一个变量的值从主内存传输到线程的工作内存。 Load作用于工作内存的变量将read操作得到的变量的值放入工作内存中的变量副本中 Use作用于工作内存的变量将工作内存中变量的值传递给执行引擎当虚拟机需要使用时会执行这个操作。 Assign作用于工作内存的变量将一个执行引擎接收到的值赋给工作内存中的变量当虚拟机遇到给变量赋值的字节码时会执行此操作。 Store将工作内存中的一个变量值传送到主内存中 Write作用于主内存的变量它把store操作从工作内存中得到的变量的值放入主内存的变量中。   重排序 从上面可以大致看出来线程之间通信的流程但是在执行程序时编译器和处理器往往会对指令进行重排序也就是不一定按照程序写的执行。这往往是为了提高性能提高并发度。但是对于多线程程序来说这往往会造成程序执行的结果不一致所以我们就得需要通过synchronizedvolatile等方式进行同步。 内存屏障 由于有重排序为了保证内存的可见性java编译器在生成的指令序列中会插入内存屏障指令来禁止特定类型的处理器重排序JMM把内存屏障指令分为下列四类 屏障类型 指令示例 说明 LoadLoad Barriers Load1; LoadLoad; Load2 确保Load1数据的装载之前于Load2及所有后续装载指令的装载。 StoreStore Barriers Store1; StoreStore; Store2 确保Store1数据对其他处理器可见刷新到内存之前于Store2及所有后续存储指令的存储。 LoadStore Barriers Load1; LoadStore; Store2 确保Load1数据装载之前于Store2及所有后续的存储指令刷新到内存。 StoreLoad Barriers Store1; StoreLoad; Load2 确保Store1数据对其他处理器变得可见指刷新到内存之前于Load2及所有后续装载指令的装载。 StoreLoad Barriers会使该屏障之前的所有内存访问指令存储和装载指令完成之后才执行该屏障之后的内存访问指令。   Happens-Before 在JMM中如果一个操作执行的结果需要对另一个操作可见那么这两个操作之间必须要存在happens-before关系这两个操作可以是一个线程之间也可以是两个线程之间。 Happens-before规则如下 1、程序顺序规则一个线程中的每个操作happens-before于该线程中的任意后续操作。 2、监视器锁规则一个监视器的解锁happens-before于随后这个监视器的加锁。 3、volatile规则对于volatile域的写happens-before于任意后续对这个volatile的读。 4、传递性如果A happens-before BB happens-before C那么A happens-before C。   Happens-before并不是说两个操作代码上执行时间的先后顺序而是保证一个操作的结果对另一个操作可见保证执行结果是顺序的。     数据依赖性   如果两个操作访问同一个变量且其中两个操作有一个是写操作此时这两个操作之间就存在数据依赖性。 数据依赖分为三种类型   写后读 a1; ba; 写后写 a1; a1; 读后写 ba; a1; 所谓的数据依赖性就是指当发生重排序的时候其结果会发生改变所以编译器和处理器在重排序的时候不会改变存在数据依赖性的两个操作的执行顺序     As-if-serial语义   不管怎么重排序单线程程序的执行结果不能被改变。所以为了遵循这种语义编译器和处理器不会对存在数据依赖性的操作进行重排序。     控制依赖性   if(flag) //---1 int i a*a; //-----2 操作1和操作2之间存在控制依赖所以在多线程的程序中编译器和处理器会启动猜测执行 处理器可以提前计算a*a的结果然后放到一个重排序的缓冲的硬件缓存中当操作1的条件为真时将计算结果写入到变量i 中。所以我们会发现在这里对两个操作做了重排序所以破坏了多线程程序的语义。 但是对于单线程而言重排序存在控制依赖的操作不会改变执行结果但是在多线程中重排序存在控制依赖的操作可能会改变执行结果。   顺序一致性   当程序未使用同步时就会出现数据竞争所以会造成结果的改变。 当使用了同步以后这便是一个没有数据竞争的程序。如果程序是正确使用同步的那么执行的程序将会具有顺序一致性即程序的执行结果与在顺序一致性模型中执行的完全相同。   所谓顺序一致性模型是一个理论的参考模型具有两大特性 一个线程中的所有操作都必须按照程序的顺序来执行 无论程序是否同步所有线程都只能看到一个单一执行顺序在顺序一致性模型中每个操作都是原子性的执行而且必须立即对其他线程可见。   注 JMM不保证对64位的long和double型的变量没有volatile修饰读写具有原子性而内存一致性模型保证对所有的读写操作都具有原子性。   因为在一些32位的处理器上如果对64位的long和double读写具有原子性那么需要很大的开销所以java不强求必须对这两种具有原子性。JVM在这些处理器上运行时会将一个64位的long/double写操作分成两个32位的写操作来执行此时对其就无法保证原子性所以有可能造成读取的时候读了一半数的错误。 volatile volatile可是看成是弱一级的Synchronized换句话说就是给volatile变量单个的读写操作使用同一个锁对这些单个的操作进行了同步。 我们来看一下volatile的效果 使用volatile [java] view plaincopy class VolatileFeaturesExample { //使用volatile声明64位的long型变量 volatile long vl 0L;   public void set(long l) { vl l; //单个volatile变量的写 }   public void getAndIncrement () { vl; //复合多个volatile变量的读/写 }   public long get() { return vl; //单个volatile变量的读 } } 使用synchronized替换volatile [java] view plaincopy class VolatileFeaturesExample { long vl 0L; // 64位的long型普通变量   //对单个的普通 变量的写用同一个锁同步 public synchronized void set(long l) { vl l; }   public void getAndIncrement () { //普通方法调用 long temp get(); //调用已同步的读方法 temp 1L; //普通写操作 set(temp); //调用已同步的写方法 } public synchronized long get() { //对单个的普通变量的读用同一个锁同步 return vl; } }   我们可以看出来对Volatile的单个读写操作与对一个普通变量的读写操作使用同一个锁来同步之间的效果是一样的。 我们发现及时64位的long和double只要是volatile那么该变量的读写就是原子性的而volatile这时复合操作所以不具有原子性。 volatile的性质可见性和原子性 可见性读取一个volatile变量的时候总是可以看到其他线程对这个变量的最后写入也就是说每次读取的都是最新值。 原子性对任意单个volatile变量操作具有原子性   我们可以从可见性得到volatile可以建立一定意义上的happens-before关系因为其写优先于读。 执行流程 当线程A写一个volatile变量的时候JMM会把该变量本地内存中的值刷新到主内存因此本地内存中的值和主内存中的是一致的。   当线程B读取一个volatileJMM会将该线程的本地内存置为无效直接从主内存中读取这样读取到的值就是刚刚写入的   也就是说线程B在读取volatile变量的时候线程A之前所有对此变量的操作都对线程B可见   换个角度我们可以这么说 线程A在写入一个volatile变量实际上是对下一个将要读取这个volatile变量的线程B发出了消息而线程B读取这个volatile变量就是接收了线程A发出的消息。 所以线程A写volatile线程B读volatile可以看成线程A通过主内存向线程B发送了消息。 如何实现 我们知道重排序那么是否会对volatile变量重排序呢JMM限制了这种变量的重排序规则   是否能重排序 第二个操作 第一个操作 普通读/写 volatile读 volatile写 普通读/写    NO volatile读 NO NO NO volatile写   NO NO   从上表中我们可以看出 如果第二个操作是volatile的写无论第一个操作是什么都不会被编译器或处理器重排序。确保volatile之前的写操作不会被排到其后面。 如果第一个操作是volatile的读无论第二个操作是什么都不会被重排序。 如果第一个操作是volatile写而第二个是volatile读不能重排序。   volatile为了实现这个规则编译器在生成字节码的时候给字节码指令前后都添加了内存屏障禁止特定类型的处理器重排序。 规则如下: 给每个volatile的写操作前面添加了StoreStore屏障 》禁止之前的普通写操作与volatile写操作进行重排序   给每个volatile的写操作后面添加了StoreLoad屏障 》禁止后面的普通读/写操作与volatile写操作重排序   给每个volatile的读操作后面添加了LoadLoad屏障 》防止后面的普通读操作和volatile读操作的进行重排序 给每个volatile的读操作后面添加了LoadStore屏障 》防止后面的写操作与volatile读操作进行重排序     在实际情况下可以省略一些屏障而且屏障的设立和处理器也有很大关系像X86仅仅会有StoreLoad屏障因为它只允许写-读操作的重排序。   锁   上文说到了锁可以保证强大的互斥性和同步性   锁同样和volatile一样也可以建立happens-before关系   比如说线程A只有释放锁了以后线程B可以获得锁因此线程释放锁之前对共享变量的修改在线程B获得锁之后都是可见的。   锁与volatile Volatile仅仅只对单个volatile变量的读写操作具有原子性而锁很强大可以保证整个临界区代码执行都具有原子性所以相对而言volatile的可伸缩性和性能比较好。   内存语义 线程A释放锁的时候JMM会把线程对应的本地内存中的共享变量刷新到主内存中。 当线程B获取锁时JMM会把线程对应的本地内存置为无效直接从主内存中读取共享变量。   类似于volatile锁也有自己的语义线程A释放锁的时候其实就是给将要获取这个锁的线程发出了消息 线程B获得锁也就是接收了线程A所发送的消息   这个过程其实就是两个线程通过主内存进行通信 锁的实现 通常是通过ReentrantLock 中的lock方法实现这个锁一般分为公平锁和非公平锁默认为非公平锁。   公平锁在释放锁的最后写volatile变量state在获取锁的时候首先读取volatile变量所以根据volatile的happens-before规则释放锁的线程写入的volatile对获取锁的线程可见。   非公平锁会调用compareAndSetStateCAS这个方法最终调到本地方法上一些内在的规定使得CAS同时具有volatile读和volatile写的内存语义。   公平锁和非公平锁 公平锁和非公平锁在释放时最后都要写一个volatile变量state 公平锁在获取时首先读取这个volatile 而非公平锁在获取时首先会用CAS更新这个volatile变量这个操作同时具有volatile读和volatile写的内存语义。   final 对于final域的读写编译器和处理器需要遵循两个重排序规则 1、在构造函数中对final域的写入与随后将此被构对象的引用赋给其他引用两者之间不能重排序。 2、初次读取一个包含final域的引用和随后读取这个final域两者之间不能重排序     写final域的重排序规则禁止把final域的写重排序到构造函数之外 编译器会在final域的写之后构造函数return之前插入一个StoreStore屏障这个屏障禁止final域的写重排序到构造函数外面 这样可以保证其他任何线程在引用对象时对象的final域已经正确初始化但是普通域有可能没有被初始化。     编译器会在读final域操作的前面插入一个loadLoad屏障。 这样可以确保在读取一个对象的final域之前一定会先读取包含这个final域的对象引用如果该引用不为null说明引用对象的final域已经被正确初始化过了     如果final域是一个引用类型那么会增加一个约束 在构造函数内对final引用对象的成员域的写入和在构造函数外把这个被构造对象的引用赋值给另一个引用变量两者之间不能重排序。   写final域的重排序规则可以确保在引用变量对其他线程可见之前该引用变量指向的对象已经在构造函数中被正确初始化过了但是还要有一个保证就是在构造函数返回之前不能让这个被构造对象的引用为其他线程所见也就是对象引用不能再构造函数中溢出。   我们看个例子 [java] view plaincopy public class FinalReferenceEscapeExample { final int i; static FinalReferenceEscapeExample obj;   public FinalReferenceEscapeExample () { i 1; //1写final域 obj this; //2 this引用在此“逸出” }   public static void writer() { new FinalReferenceEscapeExample (); }   public static void reader { if (obj ! null) { //3 int temp obj.i; //4 } } } 线程A执行write方法线程B执行read方法。   操作2使得对象在未完成构造前就对线程B可见所以这有可能使得操作2和操作1重排序然后线程B就无法正确读取到final域的值 final的实现 上面已经说过再来总结一下 写final域的重排序规则要求编译器在 final域的写之后构造函数return之前插入StroeStore屏障 读final域的重排序规则要求编译器在读final域的操作前面插入一个loadLoad屏障 总结 JMM把happens-before要求禁止的重排序分成了两种 对于会改变程序执行结果的重排序JMM要求编译器和处理器会禁止这种排序 对于不会改变程序执行结果的重排序JMM对编译器和处理器不作要求可以重排序   也可以说JMM遵循一个原则 只要不改变程序单线程程序和多线程正确同步的程序的执行结果编译器和处理器怎么优化都可以。   转载于:https://www.cnblogs.com/salansun/p/4746819.html
http://www.pierceye.com/news/156340/

相关文章:

  • .asp网站怎么做需要一个网站
  • 免费网站代码大全网站开发费入什么费用
  • 网站域名注册多少钱搜索引擎优化工具深圳
  • 学建设网站去哪里学建网站要大约多少钱
  • 网站正则表达式怎么做网站维护一般需要多久
  • 北京网站优化价格有没有做花卉种子的网站啊
  • 资源型网站建设 需要多大硬盘vi设计方案模板
  • 网站怎么做图片放映效果代码怎么生成网站
  • 怎么写代码做网站建投商务网官网
  • 江西那家做网站公司好各类网站建设
  • 做网站和服务器的大小有关吗it培训课程
  • 湖南网站建设公司 搜搜磐石网络网站推广模板
  • 网站是软件吗页网站设计
  • 网站服务器搭建及配置的具体步骤如果自己制作网站
  • 湖北餐饮网站建设做排版的网站
  • 广东省建设教育协会官方网站首页世界上最有趣的网站
  • 平面构成作品网站手机网页qq登录
  • 厦门app开发网站开发公司电话重庆网站排名外包
  • 个人备案经营网站用自己网站做邮箱域名解析
  • 为什么大公司开发网站做英文网站网站犯法吗
  • 大连网站seo陇西网站建设公司
  • 电力建设工程质监总站网站域名使用费用一年多少钱
  • 建设单位网站经费请示上海外国语大学学风建设网站
  • 优秀网站管理员wordpress淘宝客模板下载
  • 广州越秀区网站建设手工制作简单又漂亮
  • 西安商城网站开发网站建设前台后台教程
  • 网站投放天津塘沽爆炸事件
  • 360网站安全检测自己买个服务器做网站
  • 临汾市网站建设网站版式分类
  • 广东的一起(17)做网站东莞建工集团企业网站