Java并发编程volatile关键字
volatile理解
Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块 和volatile 关键字机制。volatile具有synchronized关键字的“可见性”,volatile变量对于每次使用,线程都能得到当前volatile变量的最新值,但是没有synchronized关键字的“并发正确性”,也就是说不保证线程执行的有序性。
特性
1、保证内存可见性
各个线程对主内存中共享变量的操作都是各个线程各自拷贝到自己的工作内存操作后再写回主内存中的。这就可能存在一个线程AAA修改了共享变量X的值还未写回主内存中时 ,另外一个线程BBB又对内存中的一个共享变量X进行操作,但此时A线程工作内存中的共享比那里X对线程B来说并不不可见.这种工作内存与主内存同步延迟现象就造成了可见性问题。Java提供了volatile来保证可见性,当一个变量被volatile修饰后,表示着线程本地内存无效,当一个线程修改共享变量后他会立即被更新到主内存中,其他线程读取共享变量时,会直接从主内存中读取。
2、不保证原子性
原子性在一个操作是不可中断的,要么全部执行成功要么全部执行失败。如a++,a+=1就不是原子性操作,volatile不能保证原子性。
3、禁止指令重排序
计算机在执行程序时,为了提高性能,编译器和处理器常常会做指令重排:
1. 单线程环境里面确保程序最终执行结果和代码顺序执行的结果一致.
2. 处理器在进行重新排序是必须要考虑指令之间的数据依赖性
3. 多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程使用的变量能否保持一致性是无法确定的,结果无法预测
代码
保证内存可见性
package com.raicho.mianshi.myvolatile;
public class MyVolatileVisibility {
// private int i;
private volatile int i;
public void changeI(int i) {
this.i = i;
}
public static void main(String[] args) {
// System.out.println("没有加volatile关键字");
MyVolatileVisibility myVolatile = new MyVolatileVisibility();
new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myVolatile.changeI(1);
System.out.println(Thread.currentThread().getName()+"修改了i="+myVolatile.i);
},"线程1 ").start();
System.out.println( Thread.currentThread().getName()+"访问 i = "+ myVolatile.i);
while (myVolatile.i == 0) {
}
}
}

当没有加volatile关键字时,当主线程先访问了i值为0,后线程1再进行修改i=1,回到mian线程,并不能察觉到i的值修改为1,然而一直在while循环不能结束,加上volatile关键字就能够检测到其他线程已经将i的值修改为1,结束程序。
不保证原子性
package com.raicho.mianshi.myvolatile;
public class VolatileAtomicity {
volatile int number = 0;
public void addNum(){
number++;
}
public static void main(String[] args) {
VolatileAtomicity va = new VolatileAtomicity();
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
va.addNum();
}
},String.valueOf(i)).start();
}
//等等20条线程完成
while (Thread.activeCount() >2){
Thread.yield();
}
System.out.println(Thread.currentThread().getName()+" number = "+va.number);
}
}

通过代码验证并最终number并不能达到20000,证明volatile并不保证原子性操作
解决方案
- 在addNum()方法上加锁synchronized关键字,肯定是可以解决的,但是synchronized加锁太重了,严重降低效率
- 使用AtomicInteger类
package com.raicho.mianshi.myvolatile;
import java.util.concurrent.atomic.AtomicInteger;
public class VolatileAtomicity {
volatile int number = 0;
public void addNum(){
number++;
}
AtomicInteger atomicInteger = new AtomicInteger();
public void addNumAtomicInteger(){
atomicInteger.getAndIncrement();
}
public static void main(String[] args) {
VolatileAtomicity va = new VolatileAtomicity();
for (int i = 0; i < 20; i++) {
new Thread(()->{
for (int j = 0; j < 1000; j++) {
//va.addNum();
va.addNumAtomicInteger();
}
},String.valueOf(i)).start();
}
//等等20条线程完成
while (Thread.activeCount() >2){
Thread.yield();
}
// System.out.println(Thread.currentThread().getName()+" number = "+va.number);
System.out.println(Thread.currentThread().getName()+" number = "+va.atomicInteger);
}
}

禁止指令重排序
public void mySort(){
int x=11;//语句1
int y=12;//语句2
x=x+5;//语句3
y=x*x;//语句4
}
重新排序后可能会变为
1234
2134
1324
问题:
请问语句4 可以重排后变成第一条码?
存在数据的依赖性 没办法排到第一个
单例模式中使用双重检测机制
public class SingletonDemo {
private static volatile SingletonDemo instance=null;
private SingletonDemo(){
System.out.println(Thread.currentThread().getName()+"\t 构造方法");
}
/**
* 双重检测机制
* @return
*/
public static SingletonDemo getInstance(){
if(instance==null){
synchronized (SingletonDemo.class){
if(instance==null){
instance=new SingletonDemo();
}
}
}
return instance;
}
public static void main(String[] args) {
for (int i = 1; i <=10; i++) {
new Thread(() ->{
SingletonDemo.getInstance();
},String.valueOf(i)).start();
}
}
}
在多线程下,不加volatile关键字也可能出现指令重排的情况,是线程不安全的
Java并发编程volatile关键字的更多相关文章
- Java并发编程 Volatile关键字解析
volatile关键字的两层语义 一旦一个共享变量(类的成员变量.类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了 ...
- java并发编程 volatile关键字 精准理解
1.volatile的作用 一个线程共享变量(类的成员变量.类的静态成员变量等)被volatile修饰之后,就具有以下作用: 1)并发中的变量可见性(不同线程对该变量进行操作时的可见性),即一个线程修 ...
- Java 并发:volatile 关键字解析
摘要: 在 Java 并发编程中,要想使并发程序能够正确地执行,必须要保证三条原则,即:原子性.可见性和有序性.只要有一条原则没有被保证,就有可能会导致程序运行不正确.volatile关键字 被用来保 ...
- java并发系列(六)-----Java并发:volatile关键字解析
在 Java 并发编程中,要想使并发程序能够正确地执行,必须要保证三条原则,即:原子性.可见性和有序性.只要有一条原则没有被保证,就有可能会导致程序运行不正确.volatile关键字 被用来保证可见性 ...
- 一起来看看java并发中volatile关键字的神奇之处
并发编程中的三个概念: 1.原子性 在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行. 2.可见性 对于可见性,Java提供了volati ...
- java并发:volatile关键字
java并发需要保证原子性,可见性,有序性. http://www.cnblogs.com/expiator/p/9226775.html 一.volatile关键字作用如下: 1.volatile关 ...
- Java 并发编程——volatile与synchronized
一.Java并发基础 多线程的优点 资源利用率更好 程序设计在某些情况下更简单 程序响应更快 这一点可能对于做客户端开发的更加清楚,一般的UI操作都需要开启一个子线程去完成某个任务,否者会容易导致客户 ...
- Java多线程编程——volatile关键字
(本篇主要内容摘自<Java多线程编程核心技术>) volatile关键字的主要作用是保证线程之间变量的可见性. package com.func; public class RunThr ...
- Java并发编程--Volatile详解
摘要 Volatile是Java提供的一种弱同步机制,当一个变量被声明成volatile类型后编译器不会将该变量的操作与其他内存操作进行重排序.在某些场景下使用volatile代替锁可以减少 ...
随机推荐
- CSS样式2
1.css重用 <style> .cl{ ... } .c2{ ... } .c{ ... } </style> <div class='c c2'></di ...
- java中文乱码解决之道(七)—–JSP页面编码过程
我们知道JSP页面是需要转换为servlet的,在转换过程中肯定是要进行编码的.在JSP转换为servlet过程中下面一段代码起到至关重要的作用. <%@ page language=" ...
- 网络流--最大流--EK模板
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #i ...
- DP 60题 -2 HDU1025 Constructing Roads In JGShining's Kingdom
Problem Description JGShining's kingdom consists of 2n(n is no more than 500,000) small cities which ...
- IP 基础知识全家桶,45 张图一套带走
前言 前段时间,有读者希望我写一篇关于 IP 分类地址.子网划分等的文章,他反馈常常混淆,摸不着头脑. 那么,说来就来!而且要盘就盘全一点,顺便挑战下小林的图解功力,所以就来个 IP 基础知识全家桶. ...
- leetcode必刷200题
一.数据结构相关 链表 1. 相交链表 2. 反转链表 3. 合并两个有序链表 4. 删除排序链表中的重复元素 5. 删除链表的倒数第 n 个节点 6. 两两交换链表中的节点 7. 两数相加 II 8 ...
- 【FPGA篇章一】FPGA工作原理:详细介绍FPGA实现编程逻辑的机理
欢迎大家关注我的微信公众账号,支持程序媛写出更多优秀的文章 FPGA(Field Programmable Gate Array),即现场可编程逻辑门阵列,它是作为专用集成电路(ASIC)领域中一种半 ...
- vue项目中使用bpmn-自定义platter
前情提要 经过前四篇的学习,我们能够实现bpmn基本绘图.预览.为节点加事件加颜色等效果,这一篇我们来说,如何自定义左侧工具栏(platter),首先看一下自定义前后效果图对比: 我们本次要实现的目标 ...
- flink进阶篇
Flink 面试--进阶篇 1.Flink是如何支持批流一体的? 2.Flink是如何做到高效的数据交换的? 3.Flink是如何做容错的? 4.Flink 分布式快照的原理是什么? 5.Flink ...
- IDC预测2020云服务逆势增长!云服务器已成上云首选
IDC预测2020云服务逆势增长!云服务器已成上云首选 据IDC最新预测指出,2020年IT基础设施支出今年将增长约4%,达到2370亿美元,驱动力主要来源于云服务. 受疫情的影响,不少企业开源节流, ...