java allocate_Java中volatile关键字的最全总结

一、简介

volatile是Java提供的一种轻量级的同步机制。Java 语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量,相比于synchronized(synchronized通常称为重量级锁),volatile更轻量级,因为它不会引起线程上下文的切换和调度。但是volatile 变量的同步性较差(有时它更简单并且开销更低),而且其使用也更容易出错。

二、并发编程的3个基本概念

(1)原子性

定义: 即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

原子性是拒绝多线程操作的,不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来对它进行操作。简而言之,在整个操作过程中不会被线程调度器中断的操作,都可认为是原子性。例如 a=1是原子性操作,但是a++和a +=1就不是原子性操作。Java中的原子性操作包括:

a. 基本类型的读取和赋值操作,且赋值必须是数字赋值给变量,变量之间的相互赋值不是原子性操作。

b.所有引用reference的赋值操作

c.java.concurrent.Atomic.* 包中所有类的一切操作

(2)可见性

定义:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

在多线程环境下,一个线程对共享变量的操作对其他线程是不可见的。Java提供了volatile来保证可见性,当一个变量被volatile修饰后,表示着线程本地内存无效,当一个线程修改共享变量后他会立即被更新到主内存中,其他线程读取共享变量时,会直接从主内存中读取。当然,synchronize和Lock都可以保证可见性。synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。

(3)有序性

定义:即程序执行的顺序按照代码的先后顺序执行。

Java内存模型中的有序性可以总结为:如果在本线程内观察,所有操作都是有序的;如果在一个线程中观察另一个线程,所有操作都是无序的。前半句是指“线程内表现为串行语义”,后半句是指“指令重排序”现象和“工作内存主主内存同步延迟”现象。

在Java内存模型中,为了效率是允许编译器和处理器对指令进行重排序,当然重排序不会影响单线程的运行结果,但是对多线程会有影响。Java提供volatile来保证一定的有序性。最著名的例子就是单例模式里面的DCL(双重检查锁)。另外,可以通过synchronized和Lock来保证有序性,synchronized和Lock保证每个时刻是有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。

三、锁的互斥和可见性

锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility)。

(1)互斥即一次只允许一个线程持有某个特定的锁,一次就只有一个线程能够使用该共享数据。

(2)可见性要更加复杂一些,它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的。也即当一条线程修改了共享变量的值,新值对于其他线程来说是可以立即得知的。如果没有同步机制提供的这种可见性保证,线程看到的共享变量可能是修改前的值或不一致的值,这将引发许多严重问题。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:

a.对变量的写操作不依赖于当前值。

b.该变量没有包含在具有其他变量的不变式中。

实际上,这些条件表明,可以被写入 volatile 变量的这些有效值独立于任何程序的状态,包括变量的当前状态。事实上就是保证操作是原子性操作,才能保证使用volatile关键字的程序在并发时能够正确执行。

四、Java的内存模型JMM以及共享变量的可见性

JMM决定一个线程对共享变量的写入何时对另一个线程可见,JMM定义了线程和主内存之间的抽象关系:共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存保存了被该线程使用到的主内存的副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。

909aeac5a1a5

对于普通的共享变量来讲,线程A将其修改为某个值发生在线程A的本地内存中,此时还未同步到主内存中去;而线程B已经缓存了该变量的旧值,所以就导致了共享变量值的不一致。解决这种共享变量在多线程模型中的不可见性问题,较粗暴的方式自然就是加锁,但是此处使用synchronized或者Lock这些方式太重量级了,比较合理的方式其实就是volatile。

需要注意的是,JMM是个抽象的内存模型,所以所谓的本地内存,主内存都是抽象概念,并不一定就真实的对应cpu缓存和物理内存

五、volatile变量的特性

(1)保证可见性,不保证原子性

a.当写一个volatile变量时,JMM会把该线程本地内存中的变量强制刷新到主内存中去;

b.这个写会操作会导致其他线程中的缓存无效。

(2)禁止指令重排

重排序是指编译器和处理器为了优化程序性能而对指令序列进行排序的一种手段。重排序需要遵守一定规则:

a.重排序操作不会对存在数据依赖关系的操作进行重排序。

比如:a=1;b=a; 这个指令序列,由于第二个操作依赖于第一个操作,所以在编译时和处理器运

行时这两个操作不会被重排序。

b.重排序是为了优化性能,但是不管怎么重排序,单线程下程序的执行结果不能被改变

比如:a=1;b=2;c=a+b这三个操作,第一步(a=1)和第二步(b=2)由于不存在数据依赖关系, 所以可能会发

生重排序,但是c=a+b这个操作是不会被重排序的,因为需要保证最终的结果一定是c=a+b=3。

重排序在单线程下一定能保证结果的正确性,但是在多线程环境下,可能发生重排序,影响结果,下例中的1和2由于不存在数据依赖关系,则有可能会被重排序,先执行status=true再执行a=2。而此时线程B会顺利到达4处,而线程A中a=2这个操作还未被执行,所以b=a+1的结果也有可能依然等于2。

909aeac5a1a5

.

使用volatile关键字修饰共享变量便可以禁止这种重排序。若用volatile修饰共享变量,在编译时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序,volatile禁止指令重排序也有一些规则: a.当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后 面的操作可见;在其后面的操作肯定还没有进行; b.在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放 到其前面执行。 即执行到volatile变量时,其前面的所有语句都执行完,后面所有语句都未执行。且前面语句的结果对volatile变 量及其后面语句可见。

六、volatile不适用的场景

(1)volatile不适合复合操作 例如,in c++不是一个原子性操作,可以由读取、加、赋值3步组成,所以结果并不能达到30000。

.

909aeac5a1a5

(2)解决方法

1.采用synchronized

909aeac5a1a5

2.采用Lock

909aeac5a1a5

3.采用java并发包中的原子操作类,原子操作类是通过CAS循环的方式来保证其原子性的

909aeac5a1a5

七、volatile原理

volatile可以保证线程可见性且提供了一定的有序性,但是无法保证原子性。在JVM底层volatile是采用“内存屏障”来实现的。观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令,lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能:

I. 它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内

存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;

II. 它会强制将对缓存的修改操作立即写入主存;

III. 如果是写操作,它会导致其他CPU中对应的缓存行无效。

八、单例模式的双重锁为什么要加volatile

909aeac5a1a5

需要volatile关键字的原因是,在并发情况下,如果没有volatile关键字,在第5行会出现问题。instance = new TestInstance();可以分解为3行伪代码

1.memory = allocate()  //分配内存

2. ctorInstanc(memory)  //初始化对象

3. instance = memory  //设置instance指向刚分配的地址

上面的代码在编译运行时,可能会出现重排序从1-2-3排序为1-3-2。在多线程的情况下会出现以下问题。线程A在执行第5行代码时,B线程进来,而此时A执行了1和3,没有执行2,此时B线程判断instance不为null,直接返回一个未初始化的对象。

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

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

相关文章

缩放手势 ScaleGestureDetector 源码解析,这一篇就够了

其实在我们日常的编程中,对于缩放手势的使用并不是很经常,这一手势主要是用在图片浏览方面,比如下方例子。但是(敲重点),作为 Android 入门的基础来说,学习 ScaleGestureDetector 的使用&#x…

postgres的数据库备份和恢复

备份和恢复 一条命令就可以解决很简单: 这是备份的命令: pg_dump -h 127/0.0.1 -U postgres databasename > databasename.bak 指令解释: pg_dump 是备份数据库指令,164.82.233.54是数据库的ip地址(必须保证数据库允许外部访…

java 类的执行顺序_Java中类的执行顺序

讲解在代码中:package 类执行顺序;/*** java类执行顺序** 1、如果父类有静态成员赋值或者静态初始化块,执行静态成员赋值和静态初始化块* 2、如果类有静态成员赋值或者静态初始化块,执行静态成员赋值和静态初始化块* 3、将类的成员赋予初值(原…

ZooKeeper相关资料集锦

1、ZooKeeper相关概念总结 https://github.com/Snailclimb/JavaGuide/blob/master/docs/system-design/framework/ZooKeeper.md 2、ZooKeeper在Windows下的安装和配置 https://blog.csdn.net/morning99/article/details/40426133 3、Curator框架应用 http://ifeve.com/zookeepe…

JQuery.Ajax()的data参数传递方式

最近,新学c# mvc,通过ajax post方式传递数据到controller。刚开始传递参数,controller中总是为null。现记录一下,可能不全,纯粹记个学习日记。 重点在于参数的方式,代码为例子 1、这里 dataType: "js…

java如何实现封装_java如何实现封装

Java中类的封装是如何实现的封装是将对象的信息隐藏在对象内部,禁止外部程序直接访问对象内部的属性和方法。 java封装类通过三个步骤实现: (1)修改属性的可见性,限制访问。 (2)设置属性的读取方法。 (3)在读取属性的方法中,添加对…

用了30天整理的一些GO语言学习资料,2019请你加油

因为极其优秀的并发性能,Google的亲儿子Go语言站上了风潮之巅。出现在21世纪的GO语言,虽然不能如愿对C取而代之,但是其近C的执行性能和近解析型语言的开发效率以及近乎于完美的编译速度,已经风靡全球。特别是在云项目中&#xff0…

Kubernetes网络设计原则

在配置集群网络插件或者实践K8S 应用/服务部署请时刻想到这些原则: 1.每个Pod都拥有一个独立IP地址,Pod内所有容器共享一个网络命名空间2.集群内所有Pod都在一个直接连通的扁平网络中,可通过IP直接访问 所有容器之间无需NAT就可以直接互相访问…

php token 验证,PHP如何实现Token验证

PHP如何实现Token验证首先将Token进行解析&#xff1b;然后根据解析出来的信息部分验证是否过期&#xff0c;如果未过期再将解析出的信息部分进行加密&#xff1b;最后将加密出来的数据和解析出来签名进行比对&#xff0c;如果相同则验证成功。示例代码&#xff1a;<?php f…

关于Linux fontconfig 字体库的坑

01、安装字体软件yum -y install fontconfig然后把字体拷过去就行了 cd /usr/share/fonts fc-list 这是查看02、拷贝字体到指定目录 cp simsun.ttc /usr/share/fonts/然后把字体拷过去就行了 cd /usr/share/fonts 03、验证字体安装情况 fc-list //"宋体"中文字体…

满江红.互联网

小小寰球&#xff0c;有多少信息瞬抵。互联网&#xff0c;几多涛生&#xff0c;几多云逸。蚂蚁缘槐近大国&#xff0c;菜鸟搭枝成凤翼。正台风绿叶下临安&#xff0c;何足惧?多少事&#xff0c;从来急&#xff1b;天地转&#xff0c;光阴隙。一百年太久&#xff0c;只争朝夕。…

Python startswith()函数 与 endswith函数

函数&#xff1a;startswith() 作用&#xff1a;判断字符串是否以指定字符或子字符串开头一、函数说明语法&#xff1a;string.startswith(str, beg0,endlen(string)) 或string[beg:end].startswith(str)参数说明&#xff1a;string&#xff1a; 被检测的字符串str&#xff1a;…

GitLab 在多分支中的一个push

情景&#xff1a;a.本地库新建的分支&#xff0c;而Git服务器没有这个分支服务器分支master本地新建分支&#xff1a;rdar-MS&#xff0c;并git checkout rdar-MS上masterrdar-testrdar-MS更改rdar-MS分支上的文件&#xff0c;git add .git commit -m " "后&#xf…

php post 获取xml,php 获取post的xml数据并解析示例

这篇文章主要为大家详细介绍了php 获取post的xml数据并解析示例&#xff0c;具有一定的参考价值&#xff0c;可以用来参考一下。对php获取post过来的xml数据并解析感兴趣的小伙伴&#xff0c;下面一起跟随512笔记的小编两巴掌来看看吧&#xff01;如何获取请求的xml数据,对方通…

值得一用的Windows软件

该清单仅本人使用后所作推荐&#xff0c;可能会比较主观&#xff0c;所以仅供参考哈。可能某些软件链接会失效&#xff0c;可以自行百度搜索下载即可。 杀软 火绒安全&#xff1a;国内杀毒软件的一股清流&#xff0c;界面简洁&#xff0c;无推广。现在已经开启了 5.0 公测&…

《JavaScript模式》读书笔记一:基本技巧

《JavaScript模式》的读书笔记&#xff0c;个人向&#xff01;更新进度随我的阅读进度 基本技巧 尽量少用全局变量 防止变量污染注意JS变量提升问题尽量使用单一var模式&#xff0c;只使用一个var在函数顶部进行变量声明function fun () {var a 1,b2,sum ab,函数体//} for循环…

Python字符串处理全攻略(四):常用内置方法轻松掌握

文章目录 引言Python字符串常用内置方法切片功能介绍语法示例注意事项 str.isalpha()功能介绍语法示例注意事项 str.isdigit()功能介绍语法示例注意事项总结 str.isalnum()功能介绍语法示例注意事项总结 str.isupper()功能介绍语法示例注意事项 islower()功能介绍语法示例注意事…

php空间限制磁盘限额,ORA-01536:超出表空间XXXX的空间限额

问题描述&#xff1a;在FMIS2600用户下进行某个DDL或DML操作时&#xff0c;提示&#xff1a;ORA-01536&#xff1a;超出表空间FMIS2600 的空间限额 或者 ORA-01950: 对表空间/*******************ORA-01536&#xff1a;超出表空间XXXX的空间限额*******************//*********…

01爬虫基本原理及Requests库下载

一、爬虫基本原理 1.什么是爬虫 ​ 爬虫就是爬取数据 2.什么是互联网&#xff1f; ​ 就是由一堆网络设备&#xff0c;把一台台的电脑互联在一起 3.互联网建立的目的 ​ 数据的传递和数据共享 4.什么是数据&#xff1f; ​ 例如&#xff1a; ​ 电商平台的商业信息&#xff08;…

php 怎么实现收藏功能,php收藏功能如何实现

php收藏功能如何实现php收藏功能的实现方法&#xff1a;首先创建好数据库表 &#xff1b;然后创建前台代码&#xff0c;实现登录界面&#xff1b;接着通过html实现收藏样式&#xff1b;最后使用php进行后台处理即可。推荐&#xff1a;《PHP视频教程》这是数据库表话不多说上代码…