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

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

根据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. IOS Android支持中文与本地文件的读取写入

    转自http://www.xuanyusong.com/archives/1069 和http://www.benmutou.com/archives/2094 前几天有个朋友问我为什么在IOS平台中 ...

  2. excel 常用函数

    1.统计整列唯一值的数量 =sumproduct(1/countif(offset(A1,,,COUNTA(A:A)),OFFSET(A1,,,COUNTA(A:A))))

  3. netty初探(2)

    上一篇 netty(1) 一.TCP/IP 流式传输 在上文演示了2进制流式传输引起的TCP拆包问题,这里继续演示文本型的传输问题,文本型的可以有以下几种策略 1.1 以特殊字符表示结尾 HTTP协议 ...

  4. UIDatePicker 时间滚动表

    UIDatePicker *datapicker; //时间滚动表 datapicker = [[UIDatePicker alloc]initWithFrame:CGRectMake(50, 200 ...

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

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

  6. php添加扩展插件

    给PHP安装扩展的方式有好多 一.重新编译 进入PHP源码目录./configure --prefix=/usr/local/php ...[其他编译参数] 二.通过phpize添加扩展 进入PHP源 ...

  7. selenium+python在Windows的环境搭建

    1 python下载安装 python早已安装,不再多说.因为开发使用的python2.7,所以同样使用2.7 2 打开Powershell, 输入python -m pip install sele ...

  8. Kettle 使用相关

    Kettle 作为国外的一款开源ETL软件,要使用了

  9. 使用MegaCli工具,在线调整raid配置

    公司hadoop平台采购了一批浪潮服务器,2个系统盘,12个数据盘.先想的用直通的raid卡免得再做单盘raid0麻烦,结果这批机器配的卡也不支持裸盘使用,咨询浪潮客服,说可以使用JBOD的模式进行, ...

  10. 在hadoop 的任务中设置 map数量

    试验了一下: 调整mapred-site.xml中mapred.min.split.size的值可以改变map的数量 首先设置了hdfs-site.xml中的dfs.block.size为20M,测试 ...