1. 首页
  2. > 香港公司注册 >

cas14介绍(cas140ct介绍)


目录:


[干货] JAVA - JVM - 1 JVM一套带走


[干货] JAVA - JVM - 2 内存两分


[干货] JAVA - JVM - 3 字符串常量池


[干货] JAVA - JVM - 4 类加载


[干货] JAVA - JVM - 9 内存回收


[干货] JAVA - JVM - 10 内存回收-跟搜索算法


[干货] JAVA - JVM - 11 内存回收-回收策略


[干货] JAVA - JVM - 12 内存回收-跟搜索算法的 HotSpot 实现


[干货] JAVA - JVM - 13 内存回收-HotSopt 的分代策略和收集器


[干货] JAVA - JVM - 14 内存并发- JMM Volatile CAS


[干货] JAVA - JVM - 15 内存并发-Synchronize wait notify


内存并发部分需要2章,并发和 Synchronize



两种并发模型

在命令式编程中,线程之间的通信机制有两种:共享内存消息传递


在并发编程中,我们需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体)。


Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式进行,整个通信过程对程序员完全透明。但是同步是需要程序员自己做的,比如Synchroniz wait/notify lock cas


并发带来的问题

共享对象的可见性



无论是 java 还是什么,只要是并发,就有这个问题,这是 CPU 决定的。每个核心都有自己的缓存,还有主内存,可见性问题确定一定以及肯定存在。


在 Java 中,Volatile 可以解决人尽皆知,具体是怎么解决的,稍后容禀。


竞争



这个图貌似贴的不太好,如果 obj.count =1,这就好了。和可见性问题一样,不只是 java,所有的语言在多线程中都存在这个问题,同样 volatile 解决。稍后细说。


JMM

Java内存模型即Java Memory Model,简称JMM。JMM定义了Java 虚拟机(JVM)在计算机内存(RAM)中的工作方式。JVM是整个计算机虚拟模型,所以JMM是隶属于JVM的。



JVM中运行的每个线程都拥有自己的线程栈,线程栈包含了当前线程执行的方法调用相关信息,我们也把它称作调用栈。随着代码的不断执行,调用栈会不断变化。


本地变量表


一个线程只能读取自己的线程栈,也就是说,线程中的本地变量对其它线程是不可见的。即使两个线程执行的是同一段代码,它们也会各自在自己的线程栈中创建本地变量,因此,每个线程中的本地变量都会有自己的版本。


所有原始类型(boolean,byte,short,char,int,long,float,double)的本地变量都直接保存在线程栈当中,对于它们的值各个线程之间都是独立的。对于原始类型的本地变量,一个线程可以传递一个副本给另一个线程,当它们之间是无法共享的。


堆区


堆区包含了Java应用创建的所有对象信息,不管对象是哪个线程创建的,其中的对象包括原始类型的封装类(如Byte、Integer、Long等等)。不管对象是属于一个成员变量还是方法中的本地变量,它都会被存储在堆区。


指令重排序


在执行程序时,为了提高性能,编译器和处理器会对指令做重排序。需要说明的是这个指令重排序指在单线程内部的。这将带来单线程内部的数据可见性问题。


举例:


int a = 1;


int b = 100 / a


a = 100;


这三行指令如果重排序,把第三行指令插入到第二行之前,就会导致 b 的结果不同。(老哥我也不知道这个问题应该什么名字,难不成叫脏读?姑且随个大流儿,称之为可见性问题。)


由于有了重排序,再加上数据可见性和数据竞争,导致JMM必须能完美解决这三个问题,才能既保证效率,又保证准确唯一。


为了解决上述三个问题,JMM 构建在一套规则之上,基于这套规则,满足多线程条件下的并发问题和指令重排序。


原则一:数据依赖性

如果两个操作访问同一个变量,其中一个为写操作,此时这两个操作之间存在数据依赖性。 编译器和处理器不会改变存在数据依赖性关系的两个操作的执行顺序,即不会重排序。


原则二:as-if-serial

不管怎么重排序,单线程下的执行结果不能被改变,编译器、runtime和处理器都必须遵守as-if-serial语义。


以上两个原则解决了由于重排序导致在可见性问题。


原则三:Happen-Before

我自己的理解:如果一个动作 A 在动作 B 之前发生(可以单线程,也可以在多线程中),那么动作 A 的结果一定对动作 B 可见。


l 程序顺序规则:一个线程中的每个操作,happens-before于该线程中任意的后续操作。


l 监视器锁规则:对一个锁的解锁操作,happens-before于随后对这个锁的加锁操作。


l volatile域规则:对一个volatile域的写操作,happens-before于任意线程后续对这个volatile域的读。


l 传递性规则:如果 A happens-before B,且 B happens-before C,那么A happens-before C。


内存屏障

内存屏障解决了由于指令重排序而导致的可见性问题。


通过内存屏障可以禁止特定类型处理器的重排序,从而让程序按我们预想的流程去执行。内存屏障,又称内存栅栏,是一个CPU指令,基本上它是一条这样的指令:


编译器和CPU能够重排序指令,保证最终相同的结果,尝试优化性能。插入一条Memory Barrier会告诉编译器和CPU:不管什么指令都不能和这条Memory Barrier指令重排序。


LoadLoad屏障:对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。


StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。


LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。


StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的。 在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能。


Volatile

上文说道内存屏障解决重排序的问题,Volatile 解决可见性和竞争问题。


java:


volatile Singleton instance = new Singleton();

汇编:


0x01a3de1d: movb $0x0,0x1104800(%esi);0x01a3de24: lock addl $0x0,(%esp);

movb 是赋值,lock 是个什么?


lock前缀的指令在多核处理器下会引发了两件事情:


 将当前处理器缓存行的数据会写回到系统内存。


 这个写回内存的操作会引起在其他CPU里缓存了该内存地址的数据无效。



说明:


处理器为了提高处理速度,不直接和内存进行通讯,而是先将系统内存的数据读到内部缓存(L1,L2或其他)后再进行操作,但操作完之后不知道何时会写到内存,如果对声明了Volatile变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写回到系统内存。但是就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题,所以在多处理器下,为了保证各个处理器的缓存是一致的,就会实现cache缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改操作的时候,会强制重新从系统内存里把数据读到处理器缓存里。


Volatile 和 内存屏障

有人说 volatile 是内存屏障实现的,我觉得是不正确的。volatile 会在赋值之前加入 lock 前缀,lock 前缀才保证共享内存的一致性。内存屏障有另外的指令。他们的关系仅仅是:在 volatile 附近(volatile 翻译后的指令),会有内存屏障指令,以禁止相关重排序。


CAS

CAS的全称为Compare-And-Swap,是一条CPU的原子指令,其作用是让CPU比较后原子地更新某个位置的值。


根据CPU(X86)处理器源代码所示,程序会根据当前处理器的类型来决定是否为cmpxchg指令添加lock前缀。如果程序是在多处理器上运行,就为cmpxchg指令加上lock前缀(lock cmpxchg)。反之,如果程序是在单处理器上运行,就省略lock前缀


Java 中的 CAS

public final boolean compareAndSet(boolean expect, boolean update) { int e = expect ? 1 : 0; int u = update ? 1 : 0; return unsafe.compareAndSwapInt(this, valueOffset, e, u); }


下一章内存并发-synchronize,未完待续....


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至123456@qq.com 举报,一经查实,本站将立刻删除。

联系我们

工作日:9:30-18:30,节假日休息