java多线程之volatile理解
最近一直在看多线程的一些知识,看了一些书和一些博客,收获还是挺多的,最近看了《java并发编程的艺术》这本书感觉收获很大也推荐给各位,同时也结合以前看的博客就好好的总结一下自己所学的东西吧,有不足的地方欢迎各位指正,这篇文章主要是讲volatile关键字的知识。
volatile的特性
- 可见性:volatile在多线程中能够保证共享变量的“可见性”,简单的说就是当一个线程修改了volatile变量的时候,java线程内存模型能够确保所有的线程看到的这个变量的值是一致的。
- 防止指令重排序
java内存模型
- 在学习volatile的知识之前我们先来简单了解下java内存模型(JMM)引用一张网上很经典的表示java内存模型的图
- 大概解释下这个图的意思
- 在多个线程运行的时候每一个线程(Thread)都有一个属于自己的内存空间
- 多个线程共同使用一个主存
- 每个线程在对数据进行修改之前都会先在主存里面获取相关数据,然后在自己的工作内存里面对数据进行操作。
可见性
关键的地方就来了,因为每个线程然都是在自己的内存里进行操作,然而每个线程的工作内存之间都是相互不可见的,所以对共享变量的修改并不会马上被其他线程看到,所以就会造成多个线程操作同一个数据但是最后结果并不是我们期望的结果。当线程1去首先从主存中加载一个volatile变量到自己的工作内,然后对这个volatile变量进行写操作,写入操作结束之后,volatile变量的最新值会立马刷新到主内存,同时其他线程中的这个volatile变量会立马失效,会被强迫从主内存中重新读取volatile变量的最新值,这就是volatile变量的可见性实现的过程。同时这也可以看做是一个线程和其他线程通信的一个过程。
防止指令重排序
简单解释下指令重排序,重排序指的是编译器和处理器为了优化程序的性能会对指令序列进行重新排序的一种手段。在单线程的程序里,指令的重排序会保证执行结果的正确性,但是在多线程中指令的重排序对程序的执行结果的正确性就得不到保障(指令重排序的一些规则各位可以去查阅一下,这里不赘述)。volatile变量防止指令重排序请先看下面的内存屏障介绍。
内存屏障
JMM把内存屏障分为四类(摘自Java并发编程艺术)
屏障类型 | 指令示例 | 说明 |
---|---|---|
LoadLoad Barriers | Loadl; LoadLoad; Load2 | 确保Loadl数据的装载先于Load2及所有后续装载指令 |
StoreStore Barriers | Storel; StoreStore; Store2 | 确保Store1数据对其他处理器可见(刷新到内存)先于Store2及所有后续存储指令的存储 |
LoadStore Barriers | Loadl; LoadStore; Store2 | 确保Loadl数据装载先于Store2及所有后续的存储指令刷新到内存 |
StoreLoad Barriers | Storel; StoreLoad; Load2 | 确保Storel数据对其他处理器变得可见(指刷新到内存)先于Load2及所有后续装载指令的装载。StoreLoad Barriers会使该屏障之前的所有内存访问指令(存储和装载指令)完成之后,才执行该屏障之后的内存访问指令 |
- volatile变量基于保守策略的JMM内存屏障插入策略
- 在每个volatile写操作的前面插入一个StoreStore屏障。
- 在每个volatile写操作的后面插入一个StoreLoad屏障。
- 在每个volatile读操作的后面插入一个LoadLoad屏障。
- 在每个volatile读操作的后面插入一个LoadStore屏障。
看完这个相信各位对volatile防止指令重排序就有一个比较清楚的认识了,解释一下上面的四条策略,
- 在volatile变量的写操作前面的其他写操作会在volatile变量写前面执行(提前刷新到主存,对其他线程可见)
- volatile变量的写会比后面的其他读写操作先进行
- volatile变量读操作前面的读操作会在volatile变量读操作以前进行
- volatile变量读操作后面的其他写操作会在volatile变量读操作以后进行
volatile的应用
很多博客中基本上都说了volatile变量一般运用于不依附当前值的操作,比如自增,我的理解是这样的,如果volatile变量进行依附当前操作的值的运算,那么就会涉及到读volatile变量和写volatile变量这两个操作,volatile变量的读操作(从主内存读取到线程的工作内存)和写操作(将变量写到主内存中去)时,这两个组合起来的操作就是一个非原子性的操作,所以这种情况下使用volatile关键字就不合适,对于基本变量的一些非原子性操作(如自增)可以考虑使用java.util.concurrent.atomic包下的一些类,或者使用锁来进行。volatile变量一般用作比如一个标志变量这种单个读写的操作。 附上个人觉得volatile变量应用的一个讲得比较好的博客(www.ibm.com/developerwo… 同时各位也可以去看一下另外一篇讲解volatile的博文,同样比较棒(kwsir.cn/2017/10/12/…
写在最后
鉴于本人水平有限,所以如果文章中有不对的地方,十分欢迎各位在评论留言指点,或者发送到本人的qq邮箱549005114@qq.com通知一下本人。谢谢大家。
java多线程之volatile理解相关推荐
- Java多线程之volatile详解
Java多线程之volatile详解 目录: 什么是volatile? JMM内存模型之可见性 volatile三大特性之一:保证可见性 volatile三大特性之二:不保证原子性 volatile三 ...
- JAVA多线程之volatile 与 synchronized 的比较
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:hapjin cnblogs.com/hapjin/p/54 ...
- Java多线程之CAS深入解析
Java多线程之CAS深入解析 目录: CAS是什么 CAS底层原理Unsafe深入解析 CAS缺点 引子:蚂蚁花呗一面:讲一讲AtomicInteger,为什么要用CAS而不是synchronize ...
- Java多线程之8Lock问题解析
Java多线程之8Lock问题解析 本文目录 1. 8Lock实例: 标准访问的时候,请问先打印邮件还是短信? sendEmail方法暂停4秒钟,请问先打印邮件还是短信? 新增Hello普通方法,请问 ...
- Java多线程之Runable与Thread
Java多线程是Java开发中的基础内容,但是涉及到高并发就有很深的研究可做了. 最近看了下<Java并发实战>,发先有些地方,虽然可以理解,但是自己在应用中很难下手. 所以还是先回顾一下 ...
- JAVA多线程之wait/notify
本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法. ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用 ②wait ...
- Java多线程之Callable、Future和FutureTask
Java多线程之Callable接口 自己想总结一下的,看到一篇总结的更好的博客,就转载了,突然感觉真轻松,哈哈哈哈 文章转载于:Matrix海子:Java并发编程:Callable.Future和F ...
- Java多线程之Synchronized和Lock的区别
Java多线程之Synchronized和Lock的区别 目录: 原始构成 使用方法 等待是否可以中断 加锁是否公平 锁绑定多个条件Condition 小结:Lock相比较Synchronized的优 ...
- Java多线程之CAS缺点
Java多线程之CAS缺点 目录: 循环时间开销很大 只能保证一个共享变量的原子操作 引来ABA问题及解决方案(重点) 1. 循环时间开销很大 通过看源码,我们发现有个do while,如果CAS失败 ...
最新文章
- 正则表达式分类 区别
- 5单个编译总会编译全部_5分钟读懂JavaScript预编译
- linux负载均衡(什么是负载均衡)
- 无法远程分发安装软件原因
- android 云应用开发,Android云应用开发:网络通信技术介绍
- 虚拟电路网络与数据报网络
- 初步学习Prometheus的微服务监控功能
- 新0-Day漏洞或将给Linux桌面发行版带来浩劫
- springboot 启动器和监听器的加载
- java浏览器安全设置,主编练习win7系统运行java提示“您的安全设置已阻止不可信应用程序运行”的对策...
- Atitit 文件上传功能的实现 图片 视频 目录 1. 上传原理	1 1.1. http post编码 multipart / form-data	1 1.2. 临时文件模式 最简单	2 1.3
- java开发利器 eclipse从入门到精通 pdf_Java从入门到精通(第4版)高清PDF下载
- PHP代理平台最新星外主机代理平台模板聚合多代理主机加盟网站源码程序整合一站式
- Win10取消开机密码
- appfuse mysql_Appfuse学习笔记(1)
- 关于js如何获取鼠标划过文本的内容与下标
- UE4搭建场景与特效文档—地形、水体、植被、雨雾效果
- 使用Python对股票数据进行分析
- 爬虫基础_01——正则
- 启动SparkSql,报javax.jdo.JDOFatalInternalException: Error creating transactional connection factory
热门文章
- mysql存储php数组_mysql数据库存储PHP数组、对象的方法
- Java中找出s字符串的回文_给定一个字符串 s,找到 s 中最长的回文子串。
- springMVC+hibernate + layui分页
- ie中加入html代码,IE中HTML编辑器的修改与使用
- pix4d怎么查看点云数据_PIX4D的两种像控点刺点方式探讨
- cooleditpro批量加速文件_Python玩转阿里云OSS对象存储,批量转存markdown和html图片
- seurat提取表达矩阵_本周最新文献速递20200719
- 百度爬虫爬到虚拟链接 网站被黑_什么是网络爬虫?原理是什么?种类有多少?...
- c mysql timeout_mysqltimeout知多少
- 计算机桌面程序经常打不开了,电脑开机后桌面软件打不开怎么办