范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

Java内存模型(JMM)

  在高并发的情况下,java的内存模型到底是怎么提供支持的,要说清楚这个问题我们首先要先知道一些硬件层面的的知识,因为java的内存模型都是架构在这些硬件层面上的。计算机的存储结构
  存储器的层次结构
  计算机存储结构为金字塔型存储
  这种结构主要由寄存器,缓存(cache),内存,硬盘,远程文件存储等这几部分组成。有如下两个特点:越接近金字塔顶端速度越快,容量越小,价格越贵 每一种存储器设备只和它相邻的存储设备打交道
  需要注意的是L3高速缓存是主板上被所有CPU所共享的。
  之所以是金字塔型的结构主要是局部性原理。
  程序局部性原理时间局部性
  刚被访问过的存储单元很可能不久又被访问,通常体现在循环执行的指令。让最近被访问过的信息保留在靠近CPU的存储器中加快处理速度。空间局部性
  刚被访问过的存储单元的邻近单元很有可能不久会被访问,通常体现在顺序执行的指令。将刚被访问的存储单元的邻近单元调到靠近CPU的存储器加快访问。硬件层数据一致性
  硬件层数据一致性
  我们思考这样一个问题,当CPU需要主存中读取一个X数值时,最终会被CPU1和CPU2load到自己的核心中,如果CPU1修改了X的值,设置成了1,CPU2修改了X的值设置成了2,那么就产生了数据的不一致性,那么不同核中的数据要怎么保持一致性呢?也就是说我们运用了金字塔的存储模型,速度会提高。但是会有数据不一致性的问题。要解决这个问题就需要在硬件层面进行解决。方法如下:
  总线锁
  锁住总线
  当CPU1通过总线bus访问x的值时,CPU2是不允许再访问x。因为锁住的是总线,除了访问x,访问其他的数值也是不允许的。
  总线锁会锁住总线,使得其他CPU甚至不能访问内存中其他的地址,当时此种方式效率较低。
  再老的一些CPU上会采用总线锁的方式。新的CPU采用各种各样的一致性协议。
  一致性协议
  缓存一致性协议
  这里的一致性协议会有很多, 包括MSI、MESI、MOSI Synapse、Firefly及Draqon,intel采用的是MESI,我们这里重点介绍这种协议。
  MESI(也称伊利诺斯协议)是一种广泛使用的支持写回策略的缓存一致性协议,该协议被应用在Intel奔腾系列的CPU中。
  CPU中每个缓存行使用的4种状态进行标记(使用额外的两位bit表示)
  状态
  描述
  M(Modified)修改
  这行数据有效,数据被修改了,和内存中的数据不一样,数据只存在于本cache中。
  E(Exclusive)独享/互斥
  这行数据有效,数据和内存中的数据一致,数据只存下于本Cache中
  S(Shared)共享
  这行数据有效,数据和内存中的数据一致,数据存在于很多cache中
  I(Invalid)无效
  这行数据无效
  E状态
  E状态
  只有Core 0访问变量x,它的Cache line状态为E(Exclusive)。
  S状态
  S状态
  3个Core都访问变量x,它们对应的Cache line为S(Shared)状态。
  M状态和状态之间的转化
  M状态和状态之间的转化
  Core 0修改了x的值之后,这个Cache line变成了M(Modified)状态,其他Core对应的Cache line变成了I(Invalid)状态。
  在MESI协议中,每个Cache的Cache控制器不仅知道自己的读写操作,而且也监听其它Cache的读写操作。每个Cache line所处的状态根据本核和其它核的读写操作在4个状态间进行迁移。
  需要注意的是MESI并没有完全解决锁总线的问题,我们说MESI是缓存锁,范围比起总线锁会小很多。但是有一些无法被缓存的数据或者跨越多个缓存行的数据还是需要总线锁。
  现代CPU底层的数据一致性实现是采用总线锁加缓存锁来实现的。
  缓存行
  当CPU访问某个数据时,会假设该数据附近的数据以后会被访问到,因此,第一次访问这一块区域时,会将该数据连同附近区域的数据(共64字节)一起读取进缓存中,那么 这一块数据称为一个Cache Line 缓存行 。 在一般的x86环境下一个 CacheLine  是 64  字节。
  伪共享
  缓存系统中是以缓存行(cache line)为单位存储的,当多线程修改互相独立的变量时,如果这些变量共享同一个缓存行,就会无意中影响彼此的性能,这就是伪共享。
  伪共享
  例如,CPU 1在读取数据X时,会把相邻的Y也会加载到其缓存行中,此时如果CPU2 需要读取数值Y,也会把相邻的X也加载缓存行中,此时如果CPU1修改数值X时根据缓存一致性协议会导致CPU2中的缓存行失效进而重读,如果CPU2修改数值Y时也会导致CPU1中的缓存行失效重读,这样就导致了CPU间彼此影响导致性能损耗。
  一般我们通过缓存行对齐就可以解决这样的问题。
  缓存行对齐
  基于以上问题的分析,在一些情况下,比如会频繁进行操作的数据,可以根据缓存行的特性进行缓存行对齐(即将要操作的数据凑一个缓存行进行操作)下面使用一个示例进行说明:  package com.example.demo;   public class Cacheline_nopadding {     public static class T{         //8字节         private volatile long x = 0L;     }     private static T[] arr = new T[2];       static {         arr[0] = new T();         arr[1] = new T();     }       public static void main(String[] args) throws InterruptedException {         Thread thread1 = new Thread(()->{             for(long i = 0;i < 1000_0000L;i++){                 //volatile的缓存一致性协议MESI或者锁总线,会消耗时间                 arr[0].x = i;             }         });           Thread thread2 = new Thread(()->{             for(long i = 0;i< 1000_0000L;i++){                 arr[1].x = i;             }         });         long startTime = System.nanoTime();         thread1.start();         thread2.start();         thread1.join();         thread2.join();         System.out.println("总计消耗时间:"+(System.nanoTime()-startTime)/100_000);     } }
  总计消耗时间:2508
  下面来做一个改造升级,对齐缓存行,重点代码如下 private static class Padding{         //7*8字节         public volatile long p1,p2,p3,p4,p5,p6,p7;     }     public static class T extends Padding{         //8字节         private volatile long x = 0L;     }
  通过上述代码做缓存对齐,每次都会有初始的7*8个占位,加上最后一个就是独立的一块缓存行,整理后代码如下: package com.example.demo;   public class Cacheline_padding {     private static class Padding{         //7*8字节         public volatile long p1,p2,p3,p4,p5,p6,p7;     }     public static class T extends Padding{         //8字节         private volatile long x = 0L;     }     private static T[] arr = new T[2];       static {         arr[0] = new T();         arr[1] = new T();     }       public static void main(String[] args) throws InterruptedException {         Thread thread1 = new Thread(()->{             for(long i = 0;i < 1000_0000L;i++){                 //volatile的缓存一致性协议MESI或者锁总线,会消耗时间                 arr[0].x = i;             }         });           Thread thread2 = new Thread(()->{             for(long i = 0;i< 1000_0000L;i++){                 arr[1].x = i;             }         });         long startTime = System.nanoTime();         thread1.start();         thread2.start();         thread1.join();         thread2.join();         System.out.println("总计消耗时间:"+(System.nanoTime()-startTime)/100_000);     } }
  总计消耗时间:729
  从上面可以看到,使用缓存对齐,相同操作情况下对齐后的时间比没对齐的时间减少一半。
  上面这种缓存行填充的方法在早期是比较流行的一种解决办法,比较有名的Disruptor框架就采用了这种解决办法提高性能,Disruptor是一个线程内通信框架,用于线程里共享数据。与LinkedBlockingQueue类似,提供了一个高速的生产者消费者模型,广泛用于批量IO读写,在硬盘读写相关的程序中应用十分广泛,Apache旗下的HBase、Hive、Storm等框架都有使用Disruptor。硬件级别保证有序
  乱序问题
  CPU为了提高指令的执行效率,比如在执行一条读指令(读取内存的一条数据)时,去执行另一条指令,前提是两条指令间没有依赖关系。
  我们来看这样的例子public class Disorder {     private static int x = 0, y = 0;     private static int a = 0, b =0;      public static void main(String[] args) throws InterruptedException {         int i = 0;         for(;;) {             i++;             x = 0; y = 0;             a = 0; b = 0;             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();             String result = "第" + i + "次 (" + x + "," + y + ")";             if(x == 0 && y == 0) {                 System.err.println(result);                 break;             } else {                 //System.out.println(result);             }         }     }       public static void shortWait(long interval){         long start = System.nanoTime();         long end;         do{             end = System.nanoTime();         }while(start + interval >= end);     } }
  在线程体中未发生乱序问题,那么程序输出的结果就不应该有x=0,y=0的结果,但是根据实际执行情况来看,会输出以下内容
  第3932682次 (0,0)
  也就是说,是存在乱序问题的。
  硬件内存屏障 X86sfence:在sfence指令前的写操作当必须在sfence指令后的写操作前完成lfence:在lfence指令前的读操作当必须在lfence指令后的读操作前完成mfence:在mfence指令前的读写操作当必须在mfence指令后的读写操作前完成
  原子指令
  如x86上的"lock …" 指令是一个Full Barrier,执行时会锁住内存子系统来确保执行顺序,甚至跨多个CPU。Software Locks通常使用了内存屏障或原子指令来实现变量可见性和保持程序顺序。JVM级别保证有序性
  JVM级别内存屏障
  这些都是JVM级别定义的一些规范,具体的实现都是由硬件级别的内存屏障进行保障与支持的。volatile
  volatile用来修饰成员变量(静态变量和实例变量),被修饰的变量在被修改时能够保证每个线程获取该变量的最新值,从而避免出现数据脏读的现象,也就是我们说的保证数据的可见性。volatile可以保证可见性和有序性,但是不能保证原子性。要保证原子需要synchronized和Lock。
  字节码级别
  字节码级别变化
  被volatile修饰的变量在字节码级别的access flags中会多一个volatile字符串
  JVM级别
  JVM级别
  在JVM级别就是根据volatile的写或读操作添加了相应的内存屏障。
  StoreStore屏障:禁止上面的普通写和下面的volatile写重排序;
  StoreLoad屏障:防止上面的volatile写与下面可能有的volatile读/写重排序
  LoadLoad屏障:禁止下面所有的普通读操作和上面的volatile读重排序
  LoadStore屏障:禁止下面所有的普通写操作和上面的volatile读重排序
  硬件层面
  通过相应工具查看汇编指令可以发现,在window上是通过lock指令来实现的。
  因此,volatile修饰的变量具有以下的特点:Lock前缀的指令会引起处理器缓存写回内存;一个处理器的缓存回写到内存会导致其他处理器的缓存失效;当处理器发现本地缓存失效后,就会从内存中重读该变量数据,即可以获取当前最新值。

一个老人真正的幸福手里有钱,生活简素,精神富足01hr古人说人无再少,时无再来。古人又说停车坐爱枫林晚,霜叶红于二月花。时光一去不回,谁都没有办法阻止一个人变老。这样的惆怅,令人心头泛起了一阵忧伤。生活中,有的老人唉声叹气,有进入冬月,老人喝酒忌讳多,牢记2喝2不喝,早知早受益不知不觉,天气越来越冷,眨眼就进了农历的11月,也就是冬月了。再往前数一个月,离过年也不远了,然而此时正是北方寒冬的季节,温度普遍下降得厉害。因此,不少老人喜欢喝酒御寒取暖,但出于老人下肢水肿(水潴留),中医临床处方1首,健脾温肾,利湿消肿中医认为老人由于患慢性疾病较多,脏腑功能衰退,导致脾肾阳虚,水液输布功能失调,水湿潴留,而形成下肢水肿。治疗此症,根据脾肾虚的病理特点,以健脾温肾利湿消肿为法。方组黄芪太子参,山药北京最容易偶遇明星的店要凉?夏雨杨坤投资黄渤曾站台资料图曾经风光一时的网红烘焙正处于落寞时刻。近日,有消费者反映牛角村储值卡使用受限,记者调查后发现该储值卡目前只能在直营店使用,而北京直营店仅剩两家。与此同时,其公司经营状态似乎也苦并不能让人成长,只是成长过程中的副产品文老余小区被封了,今天没上班。早上社区里与人闲聊,当说到孩子教育问题时,分歧很大甲方觉得孩子就得吃苦,这能帮助孩子成长乙方觉得并不是这样的。你同意哪一方?要让你立即给出答案或许有点宝宝为什么欢喜乱吃乱咬?2022育儿季其实这是宝宝能力发展的重要表现,通常从4个月左右开始。喜欢手吃吃脚拿起什么都往嘴里塞纸抽玩具服衣有时候连妈妈的脸蛋头发也不能幸免。出现样这的情况,就说明宝宝正于处以下知道什么是侧切吗?多少宝妈的心声你们知道什么是侧切吗?就是在孩子不太容易岀来的时候用一把大剪子咔嚓咔嚓在女人的身上剪岀一个大口子让孩子顺利岀生我劝你们不要查这是史上十大恐怖视频之一女人是弱的而男人是强的无论是生理最新研究孕妇可用针灸缓解疼痛据英国每日电讯报网站11月21日报道,根据一项新的全球数据分析结果,针灸可以显著缓解孕妇经常经历的下腰或骨盆疼痛。研究结果表明,孕妇选择用针灸缓解疼痛不会对胎儿产生明显不利的影响。江苏无锡留住城市记忆,工业遗产活化成为文旅新地标高大古朴的白墙茂密树丛间的剧场松软草地上的天幕近日,位于马山的小田茶事工坊成为不少市民朋友圈里的网红休闲去处。很多人不知道,其实这是一处由废弃油库改造而成的景观,承载着旧院的回忆,徒步苏州河畔长风湾烟囱广场,见证上海火柴工业历史时代的变迁烟囱广场最显著的建筑就是烟囱了上海试剂总厂大烟囱,高达62米,极具视觉冲击力。上海试剂总厂大烟囱是长风工业区保留下来的文物保护点,亲眼见证了百年苏州河的历史变迁。烟囱广场上有很多人新疆旅行库车游记老城墙老巷老树老风光继续新疆旅行,本期目的地库车。到新疆旅行,就像到了一个伸手就能触到历史的地方,它们就摆在街道上在库车,那些代表着古老风光的遗迹往往近在咫尺。比如库车河东岸就有汉城墙遗址,314国道
中国男篮送祝福!杜锋郭艾伦领衔,赵睿韩德君亮相,诚意满满北京时间1月31日,根据中国篮协的官方媒体透露,此前出战日本的男篮阵容名单,送出最新的祝福,每一个入选的球员都有自己的谐音,诚意满满的同时,给到球迷和媒体的诚意,也是非常具有冲击力北京冬奥会来了个神仙女孩儿,精通四国语言在北京冬奥会工作组中,有一个特别的95后女孩儿她出生在韩国,却会讲一口流利的中文,还和老师一起练太极,学习中国文化她总把青岛称作是故乡,在微博里说我是看青岛石老人长大的好是青岛最好明艳动人!中国体操冠军晒美照,梦幻绚丽让网友惊呼仙女下凡北京时间12月8日,前中国体操运动员黄秋爽通过社交媒体晒出一组美照,引发网友们强烈关注。从照片可以看到,黄秋爽在粉色玫瑰花的映衬下更加美丽,获网友们盛赞我的天!仙女来人间梦幻仙子,中国人保携手谷爱凌杨倩,助力冬奥一起向未来1月31日,在北京2022冬奥会即将开幕之际,中国人保携手品牌星荐官谷爱凌产品星荐官杨倩,一起为冬奥运动健儿加油!这一次与青年运动员达成合作共识,使中国人保在推动冰雪运动发展的同时失去世界第一宝座,陈梦最新发声不服输,抱狗捧花晒照,甜美可爱近日,国际乒联公布了最新一期世界排名,女乒的大变动引发广泛关注。久坐女乒世界第一宝座的陈梦滑落到世界第二,师妹孙颖莎首次登顶世界第一,成为首位00后世界第一。失去世界第一的排名并没湖人一败涂地,詹皇争议发推!美国网友怒了科比当老大不会这样北京时间1月31日,湖人老大詹姆斯再次引爆争议,今日洛杉矶湖人输掉比赛,而提前离队的詹皇回到洛杉矶之后却完全没有关注自己球队的情况,反而转发了一个新的动态。詹姆斯此前由于膝盖酸痛的打服太阳!勇士3大核心皆不打,网友不需要让一个布克202122新赛季NBA常规赛继续进行,一场焦点大战在实力不俗的金州勇士队与联盟第一的菲尼克斯太阳队之间展开激烈的较量。最终,实力更胜一筹的勇士队凭借史蒂芬库里维金斯追梦格林等主力恭喜!中国跳水名将汪皓晒孕照公布怀孕喜讯,肤白貌美气质出众近日,我国跳水名将汪皓在个人社交平台上晒出了自己的孕肚照,公布了怀孕的好消息,她还写道拖延症的孕妇照!值得一提的是,汪皓晒出的孕肚照非常的好看,即使有了宝宝汪皓还是那么的有气质呢!运动员大赞奥运村!网友骄傲里带点小傲娇北京冬奥会开幕在即打卡奥运村抢先刷屏!1月29日记者在闭环内发来报道美国花样滑冰名将陈巍于当晚在首都体育馆进行了抵达北京后的首次训练他说冬奥村太漂亮了,这里令人兴奋。据了解三次世锦钟鹿纯正式删除点名阿联的微博,再次发文却被网友警告别蹭大哥北京时间1月30日,CBA再次传来全新重磅消息,第二阶段的比赛好不容易结束,短暂的休赛期到来,状态逐渐恢复的广东王牌核心易建联却又遭遇到了全新争议消息。作为现在中国篮坛的一哥,大哥刷屏了!中国花滑女神隋文静晒腹肌!网友这是我做梦都不敢想的钱江晚报小时新闻记者王琼李颖北京冬奥会即将开赛,近日,花样滑冰双人滑夺冠大热门隋文静在微博晒出了一组腹肌照。这刷屏的腹肌照的背后是日复一日的训练和对即将到来的冬奥会比赛的信心,微博