同步包括两方面的含义: 独占性和可见性。

很多人仅仅理解了独占性,而忽略了可见性。

根据Java Language Specification中的说明, jvm系统中存在一个主内存(Main Memory或Java Heap Memory),Java中所有变量都储存在主存中,对于所有线程都是共享的。

每条线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。

一种常见的错误是,只有在写入共享变量时才需要同步,而读取的时候并不需要同步。

publicclass GetAndSet {

privateint i;

publicint getI() {

returni;

}

publicsynchronized void setI(int i) {

this.i = i;

}

}

在多线程环境中,仅从独占性上分析,上述程序根本不存在任何问题。

问题出在了可见性上,

即,如果调用

在线程A中:setI(10);

然后在线程B中:getI();

得到的很可能并非10,这个我们想要的答案。

这是因为并没有什么保证,在线程A设置的I,在线程B中可以立马看到当前值。

Java内存模型就是为了解决可见性的问题。

JMM规定了JVM必须遵循的一组最小保证,这组保证规定了对变量的写入操作在何时将对于其他线程是可见的。

这组保证就是Happen-Before 规则。

happen before规则: 
Each action in a thread happens before everyaction in that thread that comes later in the program's order. 
An unlock on a monitor happens before everysubsequent lock on that same monitor. 
A write to a volatile field happens before everysubsequent read of that same volatile. 
A call to start() on a thread happens before anyactions in the started thread. 
All actions in a thread happen before any otherthread successfully returns from a join() on that thread.

happen before翻译过来就是: 
程序顺序规则:一个线程中的每个操作,happens- before 于该线程中的任意后续操作。 
监视器锁规则:对一个监视器锁的解锁,happens- before 于随后对这个监视器锁的加锁。 
volatile变量规则:对一个volatile域的写,happens- before 于任意后续对这个volatile域的读。 
传递性:如果Ahappens- before B,且B happens- before C,那么A happens- before C。 
Thread.start()的调用会happens-before于启动线程里面的动作。 
Thread中的所有动作都happens-before于其他线程从Thread.join中成功返回。

Happen-Before 规则是由JVM提供的,因此无论有没有“同步协助(synchronized,lock ,volatile,final)”,程序执行时都必须遵守这些规则, 只不过这些规则中的大部分和同步协助有关系。

注意:第一条程序顺序规则,按字面意思的话,在同一个线程中所有语句的执行过程,必须按照程序流的先后去执行。不可能有任何重排序的可能。

实际情况并非如此,在同一个线程中,只有有数据相关的操作才不会被重排序,而没有任何关系的操作是可以被编译器发现,并进行适当重排的。

如:inti =10; --------------a

intj =1;----------------------b

i =i+1;----------------------c

虽然 a  hanppen  before b, 可在真实执行的时候,b有可能会在a前执行。因为该线程根本无法感知这种顺序的变化。

可是,a 不可能在c后面执行,因为这个线程本身可以感知这个顺序的变化。

jvm的可见性的理解的更多相关文章

  1. 对JDK、JRE和JVM的一些浅薄理解

    JDK:JDK(Java Development Kit),顾名思义是java程序的开发包,任何java程序想要运行都需要相应版本的JDK,可以到oracle下载(下载之后自带JRE和编译工具等,无需 ...

  2. jvm内存模型的理解

    今天周六,又开始啃一遍<深入理解java虚拟机>每次读的感觉不一样,大学代码量较少,读起来也就死记硬背. 1.堆:长度可变,运行时使用的变量:存放对象(new )和数组之类: 2.栈:长度 ...

  3. 走进JVM【二】理解JVM内存区域

    引言 对于C++程序员,内存分配与回收的处理一直是令人头疼的问题.Java由于自身的自动内存管理机制,使得管理内存变得非常轻松,不容易出现内存泄漏,溢出的问题. 不容易不代表不会出现问题,一旦内存泄漏 ...

  4. Java程序员想年后跳槽,对JVM没有深入的理解,我劝你还是别跳了

    前言 Java 虚拟机是学习 Java 的基础,也是迈入高级 Java 开发工程师的必备知识点.所以今天这篇文章我们来聊聊如何从零开始学习 Java 虚拟机. 深入浅出Java虚拟机 对于刚刚接触 J ...

  5. MVCC - Read View的可见性判断理解

    读了 @SnailMann大佬[MySQL笔记]正确的理解MySQL的MVCC及实现原理 收益颇丰,非常感谢! 但对其中如何判断事务是否可见性还是不太理解,于是作了本文,在原博客基础上,举例画图论证. ...

  6. Java的一些常见问题,JRE,JDK,JVM,包等概念理解

    Java常见错误: 文件名字应该与文件中public类的名字相同 public static void main(String[] args); 如何定位错误和解决错误. JVM,JRE,JDK解释和 ...

  7. JDK,JRE,JVM区别与联系-理解与概括

    我们利用JDK(调用JAVA API)开发了属于我们自己的JAVA程序后,通过JDK中的编译程序(javac)将我们的文本java文件编译成JAVA字节码,在JRE上运行这些JAVA字节码,JVM解析 ...

  8. Java多线程学习笔记——从Java JVM对多线程数据同步的一些理解

       我们知道在多线程编程中,我们很大的一部分内容是为了解决线程间的资源同步问题和线程间共同协作解决问题.线程间的同步,通俗我们理解为僧多粥少,在粥有限情况下,我们怎么去防止大家有秩序的喝到粥,不至于 ...

  9. java线程基础巩固---多线程与JVM内存结构的关系及Thread构造函数StackSize的理解

    继续学习一下Thread的构造函数,在上次[http://www.cnblogs.com/webor2006/p/7760422.html]已经对如下构造都已经学习过了: 多线程与JVM内存结构的关系 ...

随机推荐

  1. Mysql的转义字符

    Mysql的转义字符是"\",即反斜杠,在INSERT语句中,如果被插入的文本中包含反斜杠,那么反斜杠会被吃掉.例如: INSERT INTO tb (id,json) VALUE ...

  2. PHP中使用CURL之备忘(一)

    1.curl只能抓取页面的部分内容的原因分析: 错误提示:CURL transport error: transfer closed with outstanding read data remain ...

  3. [ An Ac a Day ^_^ ] [kuangbin带你飞]专题五 并查集 POJ 2236 Wireless Network

    题意: 一次地震震坏了所有网点 现在开始修复它们 有N个点 距离为d的网点可以进行通信 O p   代表p点已经修复 S p q 代表询问p q之间是否能够通信 思路: 基础并查集 每次修复一个点重新 ...

  4. git 基本的操作

      查看分支:git branch   查看所有分支:git branch -a   删除分支:git branch -d <name>   创建分支:git branch <nam ...

  5. tomcat解压版安装(摘自网络)

    配置Tomcat[解压版] 选择解压版的Tomcat的理由是可以让我们使用多个Tomcat,但是配置上就会出现一些问题,需要我们手动进行更改配置.我的Tomcat版本是:apache-tomcat-6 ...

  6. 【Time系列二】自动关机脚本

    今天在弄那个自动关机脚本的时候,遇到最大的麻烦就是怎么像电脑一样显示关机时间,看 了其他大神的博客,明白了原来用的是我没学过的datetime模块和time.strptime模块 ! ! ! 接下来, ...

  7. 7、Objective-C中的各种遍历(迭代)方式

    一.使用for循环 要遍历字典.数组或者是集合,for循环是最简单也用的比较多的方法,示例如下: //普通的for循环遍历 -(void)iteratorWithFor { //////////处理数 ...

  8. java栈内存堆内存和GC相关

    java栈内存堆内存 Java把内存分成两种,一种叫做栈内存,一种叫做堆内存,有着不同的作用.栈内存用来存储局部变量和方法调用.栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属 ...

  9. 从安装.net Core 到helloWord(Mac上)

    最近听说微软 正式发不了.netCore 1.0 于是就安装了 并安装了vs Code 用于编写一些.net程序 一. .netCore的安装: 首先需要通过brew安装openssl(相信homeB ...

  10. SQL语句优化技术分析 整理他人的

    一.操作符优化 1.IN 操作符 用IN写出来的SQL的优点是比较容易写及清晰易懂,这比较适合现代软件开发的风格.但是用IN的SQL性能总是比较低的,从Oracle执行的步骤来分析用IN的SQL与不用 ...