Java内存模型

一 JAVA内存模型JMM  安全

   Java的内存模型,也就是JVM所设置的内存模型。Java内存模型分为主存储器(主内存)和工做存储器(工做内存),这里的存储器与计算机硬件所讲的不同。并发

   主存储器,就是实例位置所在的区域,全部的实例都存在主存储器内,而且实例的字段也位于这里。主存储器为全部的线程所共享,主内存主要对应于Java堆中对象的实例数据部分。app

   工做存储器,它是各个线程所拥有的独立专门的做业区。在工做存储器中,存在有主存储器中必要的拷贝,称为工做拷贝或者变量副本。工做内存对应于虚拟机中的部分区域。ide

  每一个线程都位于各自的工做存储器中,每一个线程都不能直接的对存储器中字段进行引用或者赋值操做。函数

   当线程欲引用字段的值时候,会一次将值从主存储器拷贝到工做存储器中,而后再引用该工做拷贝的字段。当同一个线程再次引用同一个字段的值时候,可能会引用刚才的工做拷贝,也可能会从新从主存储器拷贝到工做存储器。测试

   当线程欲将值指定给字段的时候,会一次将值指定给位于工做存储器上的工做拷贝。指定完后,工做拷贝的内容则会映射到主存储器中。至于何时映射,是都JVM决定的。当同一个线程屡次对于同一个字段指定的时候,线程可能只会对工做拷贝进行指定,也有可能会每次指定后,立刻拷贝到主存储器中。优化


二 内存间的交互操做spa

  主内存与工做内存之间的交互操做定义了8种原子性操做。具体以下:线程

 1 lock(锁定):做用于主内存的变量,将一个变量标识为一条线程独占状态设计

  2 unlock(解锁):做用于主内存的变量,将一个处于锁定状态的变量释放出来

  3 read(读取):做用于主内存的变量,把一个变量的值从主内存传输到线程的工做内存中

  4 load(载入):做用于工做内存的变量,把read传输的变量值放入或者拷贝到工做内存的变量副本

  5 use(使用):做用于工做内存的变量,表示线程引用工做内存中的变量值,将工做内存中的一个变量的值传递给执行引擎

  6 assign(赋值):做用于工做内存的变量,表示线程将指定的值赋值给工做内存中的某个变量。

  7 store(存储):做用于工做内存的变量,把工做内存中的一个变量的值传送给主内存中

  8 write(写入):做用于主内存的变量,将store传递的变量值放入到主内存中对应的变量里


三 Java同步机制

  Java中同步包括:线程同步和内存同步。

synchronized:线程同步和内存同步

  线程的同步指的就是利用synchronized设置一个临界区,使得只有同时一个线程在该临界区执行。由synchronized所指定的临界区,来控制线程的操做。

  欲进入synchronized时候,线程的工做存储器若是有未映射到主存储器的工做拷贝,该内容就会被强制写入主存储器,而且会将工做存储器的工做拷贝所有丢弃清除掉。

  欲退出synchronized时候,线程会将工做存储器中未映射到主存储器的工做拷贝强制写入主存储器中。可是并不会清除或丢弃本身的工做存储器。

   在synchronized中,不论是方法仍是代码块,内存同步仅仅会在线程“欲进入”与“欲退出”synchronized时候进行内存同步。若是是“在synchronized内部”或“正在synchronized外部”,不必定会有内存的同步。

Volatile:内存同步

   对于关键字Volatile,它仅仅是进行内存的同步,并不会涉及线程的同步,利用Volatile修饰的字段能够容许多个线程同时访问。当线程欲引用volatile字段的值,就会从主存储器中拷贝到工做存储器里。对于指定给volatile字段值后,工做存储器的内容都会马上立刻映射到主存储器中。

    对于Volatile修饰的变量,在读取的过程与非Volatile变量差很少,只是会在写入操做上慢一点

一个变量为Volatile类型具有的两种特性:

   1 保证此变量对全部线程的可见性。指的就是当一个线程修改了这个变量的值后,新值对于其余线程来说师立刻能够看到的。

   2 禁止指令重排序优化。因为指令会在执行中进行重排序进行优化,利用Volatile后,能够保证该指定不会被从新排序,也就是在程序中位于Volatile以前的先执行,位于Volatile以后的在它以后执行,不会混合到前面或者后面指令的重排序中。

通常仅仅在如下场合中选择使用volatiole:

   1 对变量的写入操做不依赖于变量的当前值

   2 变量不会与其它状态变量一块儿归入不变性条件中

   3 在访问变量的时候不须要进行加锁

   因为long和double是64位的,JVM在处理这种类型的读写操做能够划分为两次的32位操做,因此必需要注意这两种类型的共享操做


四 原子性、可见性和有序性

  Java内存模型对于并发处理都是基于原子性、可见性和有序性进行设计的。

原子性  Java内部6种基本类型都是采用原子性操做的,当须要扩大原子性操做,就能够利用Java内存模型中的lock和unlock来实现,这两种方法对应高层次的字节码指令是monitorenter和monitorexit,而这两个字节码指令反映到Java代码中就是同步synchronized

可见性  指的就是当一个线程修改了共享变量后,其余线程都立刻看到这个变量值。在Java是利用volatile、synchronized以及final,前两种已经在前面说明了。对于final指的就是,凡是被final修饰的字段在构造器一旦被初始化完成后,那么其余线程就能看到这个final字段的值

有序性  Java运用的就是volatile和synchronized实现的,volatile保证了禁止指令重排序,保证了该指令在程序中原来的顺序。而synchronized保证了一个时刻仅容许一个线程对其进行lock操做。


五 happens-before先行发生原则

  “先行发生”,能够主要用来判断在并发中数据是否存在竞争,线程是否安全。

  “先行发生”,是Java内存模型中定义的两个操做之间的偏序关系。即操做A先行发生于操做B,那么就是在发生操做B以前,操做A产生的影响可以被操做B观察到。Java内存模型有几个先行发生的关系。在并发测试中,若是两个操做再也不如下类别中,那么其实际执行就没有顺序保障,即线程是不安全的。

程序顺序规则:若是程序中操做A在操做B以前,那么在线程的内部中A操做将在B操做以前。

监视器规则:在监视器锁上的解锁操做先行发生于后面对于同一个锁的枷锁操做。

volatile变量规则:对于一个volatile变量的写操做先行发生于对该变量的读操做以前。

线程启动规则:Thread对象的start()方法先行发生于此线程的每个动做。

线程结束规则:线程中全部的操做都先行发生于对此线程的终止测试。

线程中断规则:当一个线程在另外一个线程上被调用interrupt时,必须在被中断线程检测到interrupt调用以前执行。

终结器规则:对象的构造器函数必须在启动该对象的终结器finalize()方法以前完成。

传递性:若是操做A在操做B以前执行,操做B在操做C以前执行,那么操做A必须在操做C以前执行


注意:凡是多个线程所共享的对象,会对对象的状态进行修改等操做的时候,通常由synchronized或volatile来保护。

相关文章
相关标签/搜索