java关键字 valotile_Java内存模型-jsr133规范介绍,java中volatile关键字的含义

最近在看《深入理解Java虚拟机:JVM高级特性与最佳实践》讲到了线程相关的细节知识,里面讲述了关于java内存模型,也就是jsr 133定义的规范。

系统的看了jsr 133规范的前面几个章节的内容,觉得受益匪浅。废话不说,简要的介绍一下java内存规范。

什么是内存规范

在jsr-133中是这么定义的

A memory model describes, given a program and an execution trace of that program, whether

the execution trace is a legal execution of the program. For the Java programming language, the

memory model works by examining each read in an execution trace and checking that the write

observed by that read is valid according to certain rules.

也就是说一个内存模型描述了一个给定的程序和和它的执行路径是否一个合法的执行路径。对于java序言来说,内存模型通过考察在程序执行路径中每一个读操作,根据特定的规则,检查写操作对应的读操作是否能是有效的。

java内存模型只是定义了一个规范,具体的实现可以是根据实际情况自由实现的。但是实现要满足java内存模型定义的规范。

处理器和内存的交互

这个要感谢硅工业的发展,导致目前处理器的性能越来越强大。目前市场上基本上都是多核处理器。如何利用多核处理器执行程序的优势,使得程序性能得到极大的提升,是目前来说最重要的。

目前所有的运算都是处理器来执行的,我们在大学的时候就学习过一个基本概念 程序 = 数据 + 算法 ,那么处理器负责计算,数据从哪里获取了?

数据可以存放在处理器寄存器里面(目前x86处理都是基于寄存器架构的),处理器缓存里面,内存,磁盘,光驱等。处理器访问这些数据的速度从快到慢依次为:寄存器,处理器缓存,内存,磁盘,光驱。为了加快程序运行速度,数据离处理器越近越好。但是寄存器,处理器缓存都是处理器私有数据,只有内存,磁盘,光驱才是才是所有处理器都可以访问的全局数据(磁盘和光驱我们这里不讨论,只讨论内存)如果程序是多线程的,那么不同的线程可能分配到不同的处理器来执行,这些处理器需要把数据从主内存加载到处理器缓存和寄存器里面才可以执行(这个大学操作系统概念里面有介绍),数据执行完成之后,在把执行结果同步到主内存。如果这些数据是所有线程共享的,那么就会发生同步问题。处理器需要解决何时同步主内存数据,以及处理执行结果何时同步到主内存,因为同一个处理器可能会先把数据放在处理器缓存里面,以便程序后续继续对数据进行操作。所以对于内存数据,由于多处理器的情况,会变的很复杂。下面是一个例子:

初始值 a = b = 0

process1 process2

1:load a 5:load b

2:write a:2 6:add b:1

3:load b 7: load a

4:write b:1 8:write a:1

假设处理器1先加载内存变量a,写入a的值为2,然后加载b,写入b的值为1,同时 处理2先加载b,执行b+1,那么b在处理器2的结果可能是1 可能是3。因为在load b之前,不知道处理器1是否已经吧b写会到主内存。对于a来说,假设处理器1后于处理器2把a写会到主内存,那么a的值则为2。

而内存模型就是规定了一个规则,处理器如何同主内存同步数据的一个规则。

内存模型介绍

在介绍java内存模型之前,我们先看看两个内存模型

Sequential Consistency Memory Model:连续一致性模型。这个模型定义了程序执行的顺序和代码执行的顺序是一致的。也就是说 如果两个线程,一个线程T1对共享变量A进行写操作,另外一个线程T2对A进行读操作。如果线程T1在时间上先于T2执行,那么T2就可以看见T1修改之后的值。

这个内存模型比较简单,也比较直观,比较符合现实世界的逻辑。但是这个模型定义比较严格,在多处理器并发执行程序的时候,会严重的影响程序的性能。因为每次对共享变量的修改都要立刻同步会主内存,不能把变量保存到处理器寄存器里面或者处理器缓存里面。导致频繁的读写内存影响性能。

Happens-Before Memory Model : 先行发生模型。这个模型理解起来就比较困难。先介绍一个现行发生关系 (Happens-Before Relationship)

如果有两个操作A和B存在A Happens-Before B,那么操作A对变量的修改对操作B来说是可见的。这个现行并不是代码执行时间上的先后关系,而是保证执行结果是顺序的。看下面例子来说明现行发生

A,B为共享变量,r1,r2为局部变量

初始 A=B=0

Thread1 | Thread2

1: r2=A | 3: r1=B

2: B=2 | 4: A=2

凭借直观感觉,线程1先执行 r2=A,则r2=0 ,然后赋值B=1,线程2执行r1=B,由于线程1修改了B的值为1,所以r1=1。但是在现行发生内存模型里面,有可能最终结果为r1 = r2 = 2。为什么会这样,因为编译器或者多处理器可能对指令进行乱序执行,线程1 从代码流上面看是先执行r2 = A,B = 1,但是处理器执行的时候会先执行 B = 2 ,在执行 r2 = A,线程2 可能先执行 A = 2 ,在执行r1 = B,这样可能 会导致 r1 = r2 = 2。

那我们先看看先行发生关系的规则

1 在同一个线程里面,按照代码执行的顺序(也就是代码语义的顺序),前一个操作先于后面一个操作发生

2 对一个monitor对象的解锁操作先于后续对同一个monitor对象的锁操作

3 对volatile字段的写操作先于后面的对此字段的读操作

4 对线程的start操作(调用线程对象的start()方法)先于这个线程的其他任何操作

5 一个线程中所有的操作先于其他任何线程在此线程上调用 join()方法

6 如果A操作优先于B,B操作优先于C,那么A操作优先于C

解释一下以上几个先行发生规则的含义

规则1应该比较好理解,因为比较适合人正常的思维。比如在同一个线程t里面,代码的顺序如下:

thread 1

共享变量A、B

局部变量r1、r2

代码顺序

1: A =1

2: r1 = A

3: B = 2

4: r2 = B

执行结果 就是 A=1 ,B=2 ,r1=1 ,r2=2

因为以上是在同一个线程里面,按照规则1 也就是按照代码顺序,A = 1 先行发生 r1 =A ,那么r1 = 1

再看规则2,下面是jsr133的例子

1ce579339c3e788f7be8455715c6822d.png

按照规则2,由于unlock操作先于发生于lock操作,所以X=1对线程2里面就是可见的,所以r2 = 1

a3ff016d6f3cf8a3fe363e3dbbaff761.png

在分析以下,看这个例子,由于unlock操作先于lock操作,所以线程x=1对于线程2不一定是可见(不一定是现行发生的),所以r2的值不一定是1,有可能是x赋值为1之前的那个状态值(假设x初始值为0,那么此时r2的值可能为0)

对于规则3,我们可以稍微修改一下我们说明的第一个例子

A,B为共享变量,并且B是valotile类型的

r1,r2为局部变量

初始 A=B=0

Thread1 | Thread2

1: r2=A | 3: r1=B

2: B=2 | 4: A=2

那么r1 = 2, r2可能为0或者2

因为对于volatile类型的变量B,线程1对B的更新马上线程2就是可见的,所以r1的值就是确定的。由于A是非valotile类型的,所以值不确定。

规则4,5,6这里就不解释了,知道规则就可以了。

可以从以上的看出,先行发生的规则有很大的灵活性,编译器可以对指令进行重新排序,以便满足处理器性能的需要。只要重新排序之后的结果,在单一线程里面执行结果是可见的(也就是在同一个线程里面满足先行发生原则1就可以了)。

java内存模型是建立在先行发生的内存模型之上的,并且再此基础上,增强了一些。因为现行发生是一个弱约束的内存模型,在多线程竞争访问共享数据的时候,会导致不可预期的结果。有一些是java内存模型可以接受的,有一些是java内存模型不可以接受的。具体细节这里面就不详细说明了。这里只说明关于java新的内存模型重要点。

final字段的语义

在java里面,如果一个类定义了一个final属性,那么这个属性在初始化之后就不可以在改变。一般认为final字段是不变的。在java内存模型里面,对final有一个特殊的处理。如果一个类C定义了一个非static的final属性A,以及非static final属性B,在C的构造器里面对A,B进行初始化,如果一个线程T1创建了类C的一个对象co,同一时刻线程T2访问co对象的A和B属性,如果t2获取到已经构造完成的co对象,那么属性A的值是可以确定的,属性B的值可能还未初始化,

下面一段代码演示了这个情况

public class FinalVarClass {

public final int a ;

public int b = 0;

static FinalVarClass co;

public FinalVarClass(){

a = 1;

b = 1;

}

//线程1创建FinalVarClass对象 co

public static void create(){

if(co == null){

co = new FinalVarClass();

}

}

//线程2访问co对象的a,b属性

public static void vistor(){

if(co != null){

System.out.println(co.a);//这里返回的一定是1,a一定初始化完成

System.out.println(co.b);//这里返回的可能是0,因为b还未初始化完成

}

}

}

为什么会发生这种情况,原因可能是处理器对创建对象的指令进行重新排序。正常情况下,对象创建语句co = new FinalVarClass()并不是原子的,简单来说,可以分为几个步骤,1 分配内存空间 2 创建空的对象 3 初始化空的对象 4 把初始化完成的对象引用指向 co ,由于这几个步骤处理器可能并发执行,比如3,4 并发执行,所以在create操作完成之后,co不一定马上初始化完成,所以在vistor方法的时候,b的值可能还未初始化。但是如果是final字段,必须保证在对应返回引用之前初始化完成。

volatile语义

对于volatile字段,在现行发生规则里面已经介绍过,对volatile变量的写操作先于对变量的读操作。也就是说任何对volatile变量的修改,都可以在其他线程里面反应出来。对于volatile变量的介绍可以参考 本人写的一篇文章 《java中volatile关键字的含义》 里面有详细的介绍。

volatile在java新的内存规范里面还加强了新的语义。在老的内存规范里面,volatile变量与非volatile变量的顺序是可以重新排序的。举个例子

public class VolatileClass {

int x = 0;

volatile boolean v = false;

//线程1write

public void writer() {

x = 42;

v = true;

}

//线程2 read

public void reader() {

if (v == true) {

System.out.println(x);//结果可能为0,可能为2

}

}

}

线程1先调用writer方法,对x和v进行写操作,线程reader判断,如果v=true,则打印x。在老的内存规范里面,可能对v和x赋值顺序发生改变,导致v的写操作先行于x的写操作执行,同时另外一个线程判断v的结果,由于v的写操作先行于v的读操作,所以if(v==true)返回真,于是程序执行打印x,此时x不一定先行与System.out.println指令之前。所以显示的结果可能为0,不一定为2

但是java新的内存模型jsr133修正了这个问题,对于volatile语义的变量,自动进行lock 和 unlock操作包围对变量volatile的读写操作。那么以上语句的顺序可以表示为

thread1 thread2

1 :write x=1 5:lock(m)

2 :lock(m) 6:read v

3 :write v=true 7:unlock(m)

4 :unlock 8 :if(v==true)

9: System.out.print(x)

由于unlock操作先于lock操作,所以x写操作5先于发生x的读操作9

以上只是jsr规范中一些小结行的内容,由于jsr133规范定义了很多术语以及很多推论,上述只是简单的介绍了一些比较重要的内容,具体细节可以参考jsr规范的public view :http://today.java.net/pub/a/today/2004/04/13/JSR133.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/440502.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java重载和重写6_深入理解Java中的重写和重载

深入理解Java中的重写和重载重载(Overloading)和重写(Overriding)是Java中两个比较重要的概念。但是对于新手来说也比较容易混淆。本文通过两个简单的例子说明了他们之间的区别。定义重载简单说,就是函数或者方法有同样的名称,但是参数列表不相同的情形&…

python 协程池gevent.pool_进程池\线程池,协程,gevent

目录1. 进程池与线程池2. 协程3. gevent4. 单线程下实现并发的套接字通信首先写一个基于多线程的套接字服务端:from socket import *from threading import Threaddef comunicate(conn):while True: # 通信循环try:data conn.recv(1024)if len(data) 0: breakconn.send(data.…

java poi 3.13_Java 读取Excl文件 (poi-3.13)

最近做项目遇到了读取Excel数据到数据库做数据的初始化。于是找一个。发现(poi-3.13)可以解决问题。可以解析两种格式(xlsx和xls)以下是实现的步骤1.下载poi3.13包,地址(http://poi.apache.org/download.html#POI-3.13)2.学习APi。接下来是还是demo来说明问题吧&…

【CodeChef - CLIQUED 】Bear and Clique Distances(建图,缩点技巧,思维)

题干: 解题报告: 主要就是在于怎么处理那个前K个点:组成一个团。换句话说,缩成一个点。先直接当成每个点多了k条边来处理,T了。想想也是啊,要是K1e5,那就是1e10条边了。。刚开始尝试了半天缩点&…

【HDU - 5649】DZY Loves Sorting(线段树,区间更新区间查询,思维,01缩数变换,线段树分割)

题干: DZY has a sequence a[1..n]a[1..n]. It is a permutation of integers 1∼n1∼n. Now he wants to perform two types of operations: 0lr0lr: Sort a[l..r]a[l..r] in increasing order. 1lr1lr: Sort a[l..r]a[l..r] in decreasing order. After doin…

php错误403_phpstudy访问文件报错403/Forbidden解决办法

使用phpstudy访问WWW目录下的文件时,浏览器提示Forbidden错误,没有访问权限。我在网上搜索了喝多资料以及本人亲自尝试过后,总结了一下两种方法。方法一:打开phpStudy,点击按键“其他选项菜单”>找到phpStudy配置&g…

【CodeForces - 294B】Shaass and Bookshelf(枚举,贪心,思维,组内贪心组间dp)

题干: Shaass has n books. He wants to make a bookshelf for all his books. He wants the bookshelfs dimensions to be as small as possible. The thickness of the i-th book is ti and its pages width is equal to wi. The thickness of each book is eith…

mac php开发集成环境,MAC OS X下php集成开发环境mamp

之前苦于mac上搭建本地服务器之艰辛,找寻好久都没找到一款类似windows上集成的本地服务器环境,诸如phpstudy,xampp,appserv,虽说xampp也有mac版,但不知为何不是Apache启动不了,这里小编为大家分享了MAC OS X 下php集成…

php获取手机目录,php如何获取手机型号

手机App中判断平台,可以根据$_SERVER[HTTP_USER_AGENT]中的内容来判断浏览器类型或手机平台。(推荐学习:PHP编程从入门到精通)iPhone UA:Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, l…

【CodeForces - 920E】Connected Components? (dsu,补图连通块,STLset+map,bfs 或bitset)

题干: You are given an undirected graph consisting of n vertices and edges. Instead of giving you the edges that exist in the graph, we give you m unordered pairs (x, y) such that there is no edge between x and y, and if some pair of vertices…

【牛客 - 551F】CSL 的神奇序列(推公式,猜结论,母函数)

题干: 链接:https://ac.nowcoder.com/acm/contest/551/F 来源:牛客网 题目描述 CSL 有一个神奇的无穷实数序列,他的每一项满足如下关系: 对于任意的正整数 n ,有 , 并且 。 CSL 很清楚这样的序列是唯…

java什么时候创建进程,Java创建进程

Java创建进程1 进程的概念 11.1 进程的概念 11.2 进程的特征 11.3 进程与线程区别 12 进程的创建 12.1 JAVA进程的创建 12.1.1 ProcessBuilder 22.1.2 Runtime 32.1.3 Process 42.2 实例 52.2.1 创建子进程 52.2.2 进程阻塞问题 72.2.3 在java中执行java程序 111 进程的概念1.1…

【牛客 - 157E】青蛙(floyd最短路,建图)

题干: 链接:https://ac.nowcoder.com/acm/contest/157/E 来源:牛客网 题目描述 有一只可爱的老青蛙,在路的另一端发现了一个黑的东西,想过去一探究竟。于是便开始踏上了旅途 一直这个小路上有很多的隧道&#xff0…

php移动端url,什么是PC和移动端URL路径规范化

什么叫PC和手机端URL途径规范性在网址百度搜索引擎提升的全过程中,会牵涉到途径方位的难题。网址中的同一个网页页面只相匹配一个网站地址。一个规范化和简易的网站地址有利于检索和捕捉客户的记忆力,回绝好几条途径,偏向同一个网页页面&…

matlab的diray在哪,matlab笔记

matlab笔记 目录 P5第一章——matlab 概述与格式 P10eps 浮点相对精度inf 无穷大i 或 j 虚数单位pi 圆周率nan 非数nargin 函数输入变量数目nargout 函数输出变量数目realmax 最大正实数realmin 最小正实数real( ) 实部imag( ) 虚部abs( ) 绝对值angle( ) 复数的相位角**matlab…

【CodeForces - 190E】Counter Attack (补图bfs,卡常,dsu)

题干&#xff1a; 无向图中给定n个顶点&#xff0c;m条不存在的边(除了这m条边&#xff0c;其余都存在)&#xff0c;求图的连通分量&#xff0c;及每个连通分量的大小。 解题报告&#xff1a; https://codeforces.com/blog/entry/4556 AC代码&#xff1a; #include<cstd…

matlab 多径 时变 信道 冲击响应,无线信道—时变冲激响应

图1无线信道的作用可以分成大尺度效应和小尺度效应。大尺度的效应就是改变了信号的平均功率&#xff0c;即B点的功率是A点的1/L。因此可以将图1等效成图2图2其中C点的平均功率等于B点的平均功率。L的数值可根据传播模型确定。影响接收机性能的只是信噪比&#xff0c;因此&#…

matlab中均线交易策略,【每日一策】Matlab量化交易策略之 均线选股策略

策略名称&#xff1a;均线选股策略策略说明&#xff1a;对沪深300全市场扫描买入条件&#xff1a;1 短均线大于长均线2 最近N个交易日短均线大于长均线的次数满足某个阈值3 当前交易日的长均线值处于某个高位出场条件&#xff1a;止损&#xff1a;价格跌破入场价的一定百分比止…

docker php 乱码,如何解决docker安装zabbix5.0界面乱码

如何解决docker安装zabbix5.0界面乱码&#xff1f;zabbix图形界面乱码如下&#xff1a;解决&#xff1a;docker部署zabbix-web和源码安装zabbix-web一样&#xff0c;字体都是存储在/usr/share/zabbix/assets/fonts/1、从windown拷贝simkai.ttf(楷体)文件到docker的zabbix-web里…

【POJ - 1330】Nearest Common Ancestors(lca,模板题)

题干&#xff1a; A rooted tree is a well-known data structure in computer science and engineering. An example is shown below: In the figure, each node is labeled with an integer from {1, 2,...,16}. Node 8 is the root of the tree. Node x is an ancestor o…