大家好,我是奇喵小屋,一个分享技术和生活的博主。以下是我的主页。各首页同步更新优质博客。创建起来并不容易。请点此博关注掘金主页知乎主页Segmentfault主页简书主页会发布更多MySQL、Redis、并发、JVM、分布式等面试热点知识,以及Java学习路线、面试重点、职业规划、面对面-face等相关博客转载请注明出处!为了保证并发编程的特性不被破坏,提供了以下模型1.顺序一致性模型顺序一致性模型可以保证并发编程的特性不被破坏,为多线程提供了强大的内存一致性保证。线程程序1.1特点一个线程中的所有操作必须按照程序的顺序执行(无论程序是否同步)所有线程只能看到相同的操作执行顺序,所有对内存的操作都是原子的,其他线程是立即可见只有一个全局内存。在某一时刻,只有一个线程可以访问内存。多线程并发时,串行访问内存。1.2例子1.2.1同步程序通过加锁实现线程A和线程B的同步。同步,操作的执行一般是无序的,但是两个线程都可以看到执行的顺序(因为顺序一致性模型保证了每个线程的操作对其他线程立即可见)2.Java内存模型(JMM)2.1什么是JMMJMM是一个抽象的概念,现实中是不存在的。JMM是定义一组规则的规范。排序和处理器执行代码乱序等引起的问题。JMM的要求没有顺序一致性模型那么严格。它的方针是:对于一个正确同步的程序,在不改变程序执行结果的情况下,尽可能地为编译器和处理器优化打开方便之门JVM按照JMM2.2实现JMM是做什么的?每个处理器的内存模型不同,JMM屏蔽了不同处理器内存模型的差异。平台为Java程序员呈现一致的内存模型JMM提供内存可见性保证单线程程序不存在内存可见性问题适当同步的多线程程序JMM通过限制编译器和处理器重排序提供内存可见性保证JMM保证执行正确同步的多线程程序在任何处理器平台上都具有顺序一致性(程序的执行结果与顺序一致性内存模型中程序的执行结果相同)未同步/未正确同步的多线程程序JMM提供了一个它的最低安全保证:线程执行行时读取的值要么是前一个线程写入的值,要么是默认值JMM程序的执行结果与数据一致性模型中的执行结果一致2.3JMM描述的规则定义线程和主存摘要relationshipbetween定义了8种内存交互操作以及线程之间如何通信(线程之间的通信由JMM控制,JMM决定一个线程何时写入对另一个线程可见的共享变量)以指令ReorderingRestrictions2.3.1抽象线程之间的关系和主存线程间的共享变量存储在主存中每个线程都有一个私有的本地内存(抽象概念),本地内存存储线程需要使用的东西2.3.2八种内存交互锁(lock),作用onvariablesinthemainmemory,将变量标记为线程独占状态读(read),作用于主存中的变量,将变量的值从主存转移到线程的工作内存中,以备下次使用负载运行。加载(load),作用于工作内存的变量,将读取操作主内存的变量放入工作内存的变量副本中。use(使用),作用于工作内存的变量,将工作内存中的变量传递给执行引擎。每当虚拟机遇到需要使用变量值的字节码指令时,就会执行这个操作。assign(赋值),作用于工作内存的变量,它将一个从执行引擎接收到的值赋值给工作内存的变量副本,每当虚拟机遇到赋值给该变量的字节码指令时就会执行这个操作的变量。Store(存储),作用于工作内存的变量,它将一个变量的值从工作内存中转移到主内存中,供后续写入使用。写入(write):作用于主存中的变量,它将store操作从工作内存中得到的变量值放入主存中的变量中。解锁(unlock):作用于主存的变量,它释放一个处于锁定状态的变量,释放的变量可以被其他线程锁定。规则不允许read、load、store、write操作中的任何一个单独出现,即read操作必须在load之后,store操作必须在write之后。不允许线程丢弃自己最近的assign操作,即工作内存中的变量数据发生变化后,必须通知主内存。如果没有从工作内存分配到主内存,线程不允许同步数据。一个新的变量必须在主内存中创建,工作内存不允许直接使用一个未初始化的变量。也就是说,在对变量执行use和store操作之前,必须先经过load和assign操作。一次只有一个线程可以锁定一个变量。多次加锁后,必须执行相同次数的解锁才能解锁。如果对某个变量进行锁操作,则所有工作内存中该变量的值都会被清空。在执行引擎使用这个变量之前,变量的值必须通过加载或赋值操作重新初始化。如果变量未锁定,则无法解锁。您也不能解锁被另一个线程锁定的变量。在线程解锁变量之前,它必须首先将变量同步回主存。2.3.3线程之间如何通信JMM要求线程之间的通信必须经过主存。如果线程A和线程B要通信,那么需要两步。线程A在本地内存修改后,刷新到主内存。主内存同步到线程B的本地内存后,由线程B读取。2.3.4指令重排序的限制对于编译器重排序,JMM的编译器重排序规则禁止特定类型的编译器重排序。对于处理器重排序,JMMJava编译器的处理器重排序规则会要求Java编译器在生成指定序列时插入特定类型的内存屏障指令,并使用内存屏障指令禁止特定类型的处理器对内存屏障进行重排序.屏障类型指令示例说明LoadLoadBarriersLoad1;加载加载;Load2禁止指令重排序,Load1必须先于Load2StoreStoreBarriersStore1;商店商店;Store2禁止指令重排序,store1必须先于store2;store1写入的数据会立即同步到主存,所有线程可见LoadStoreBarriersLoad1;加载存储;Store2禁止指令重排序,Load1必须先于Store2StoreLoadBarriersStore1;存储负载;Load2禁止指令重排序,Store必须先于Load2;store1写入的数据会立即同步到主存,所有线程可见StoreLoadBarriers是一个通用的barrier,具有其他3个barrier的作用,但是开销大(因为buffer中的数据需要被flush到内存)3.happens-before规则3.1为什么要有happens-before?JMM定义的规则难以理解和编程,happens-before方便程序编写每条happens-before规则对应JMM定义的多条规则。程序员希望基于强内存模型编写程序,但JMM实现了弱内存模型(JMM),以最大限度地提高处理器性能。不改变程序运行结果的重排序是允许的),所以JMM提供了happens-before规则来为程序员提供足够的内存可见性保证(虽然有些保证不一定存在,例如:如果一个代码块的锁只会被获取一个线程,那么这个锁是可以消除的,但是从程序员的角度来看是加锁的)3.2happens-before的定义如果一个操作happens-before另一个操作操作,那么第一个操作的执行结果将对第二个操作可见,并且第一个操作的执行顺序在第二个操作之前(这只是JMM给程序员的保证,让程序员认为正确的同步多线程程序是按照happens-before指定的顺序执行的,但实际上不一定)两个操作之间存在happens-before关系,并不代表一定要指定Java平台的具体实现按照happens-before关系如果重排序后的执行结果和按照happens-before关系执行的结果一致,那么JMM允许这种重排序3.3happens-before内容程序顺序规则:一个线程中的每个操作,happens-在此线程中的任何后续操作之前。监控锁规则:解锁一个锁,happens-before在后续锁的加锁中。易失性变量规则:对易失性字段的写入发生在对易失性字段的任何后续读取之前。传递性:如果Ahappens-beforeB,且Bhappens-beforeC,则Ahappens-beforeC。()operationofthreadAhappens-beforeanyoperationinthreadB.join()规则:如果线程A执行了操作ThreadB.join()并成功返回,则线程B中的任何操作happens-before线程A成功返回ThreadB.join()操作3.顺序一致性模型和JMM区别在于顺序一致性模型是强内存模型,而JMM是弱内存模型。顺序一致性模型保证单个线程内的操作按照程序的顺序执行,而JMM不保证(即使程序正确同步,临界区的代码也可以重新排序)(不管程序是否是否同步)在顺序一致性模型中,所有线程只能看到相同的操作执行顺序(JMM不保证,因为JMM中的线程修改本地内存中的数据,其他线程在刷新到主存之前是不可见的。)顺序一致性模型保证对内存的读写是原子的,JMM不保证对64位double或long数据的读写是原子的
