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并不保证原子性操作

解决方案

  1. 在addNum()方法上加锁synchronized关键字,肯定是可以解决的,但是synchronized加锁太重了,严重降低效率
  2. 使用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关键字的更多相关文章

  1. Java并发编程 Volatile关键字解析

    volatile关键字的两层语义 一旦一个共享变量(类的成员变量.类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了 ...

  2. java并发编程 volatile关键字 精准理解

    1.volatile的作用 一个线程共享变量(类的成员变量.类的静态成员变量等)被volatile修饰之后,就具有以下作用: 1)并发中的变量可见性(不同线程对该变量进行操作时的可见性),即一个线程修 ...

  3. Java 并发:volatile 关键字解析

    摘要: 在 Java 并发编程中,要想使并发程序能够正确地执行,必须要保证三条原则,即:原子性.可见性和有序性.只要有一条原则没有被保证,就有可能会导致程序运行不正确.volatile关键字 被用来保 ...

  4. java并发系列(六)-----Java并发:volatile关键字解析

    在 Java 并发编程中,要想使并发程序能够正确地执行,必须要保证三条原则,即:原子性.可见性和有序性.只要有一条原则没有被保证,就有可能会导致程序运行不正确.volatile关键字 被用来保证可见性 ...

  5. 一起来看看java并发中volatile关键字的神奇之处

    并发编程中的三个概念: 1.原子性 在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行. 2.可见性 对于可见性,Java提供了volati ...

  6. java并发:volatile关键字

    java并发需要保证原子性,可见性,有序性. http://www.cnblogs.com/expiator/p/9226775.html 一.volatile关键字作用如下: 1.volatile关 ...

  7. Java 并发编程——volatile与synchronized

    一.Java并发基础 多线程的优点 资源利用率更好 程序设计在某些情况下更简单 程序响应更快 这一点可能对于做客户端开发的更加清楚,一般的UI操作都需要开启一个子线程去完成某个任务,否者会容易导致客户 ...

  8. Java多线程编程——volatile关键字

    (本篇主要内容摘自<Java多线程编程核心技术>) volatile关键字的主要作用是保证线程之间变量的可见性. package com.func; public class RunThr ...

  9. Java并发编程--Volatile详解

    摘要      Volatile是Java提供的一种弱同步机制,当一个变量被声明成volatile类型后编译器不会将该变量的操作与其他内存操作进行重排序.在某些场景下使用volatile代替锁可以减少 ...

随机推荐

  1. 安装并使用pyecharts库

    在cmd命令行中输入安装命令, pyecharts库的安装命令如下: pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyecharts ...

  2. JS编程建议——11:慎重使用伪数组

    建议11:慎重使用伪数组JavaScript没有真正的数组,因此typeof运算符不能辨别数组和对象.伪数组在JavaScript中有很高的易用性,程序员不用给它设置维度,而且永远不用担心产生越界错误 ...

  3. 图论--2-SAT--poj 3678-Katu Puzzle(模板题)

    Description Katu Puzzle is presented as a directed graph G(V, E) with each edge e(a, b) labeled by a ...

  4. 10.秋招复习简单整理之CSMA/CD协议

    我们知道,总线上只要有一台计算机在发送数据,总线的传输资源就被占用.因此,在同一时间只能允许一台计算机发送数据,否则各计算机之间就会相互干扰,使得所发送的数据被破坏.因此,如何协调总线上各计算机的工作 ...

  5. jmeter4.0,启动jmeter.bat闪退问题

    问题描述: 电脑重装win10系统,配置好了java环境后,解压jmeter的zip包,然后按照网上的教程配置环境变量,然后兴冲冲启动jmeter.bat,结果,闪退,甚至连个报错信息都没有... 然 ...

  6. Re模块的 三个方法

    import re s1 = '绿茶白茶黄茶青茶红茶黑茶' s2 = '中国绿茶白茶黄茶青茶红茶黑茶' ret = re.findall(".茶", s1) print(ret) ...

  7. RabbitMQ的使用(二)- RabbitMQ服务在单机中做集群

    RabbitMQ的使用(二)- RabbitMQ服务在单机中做集群 作者:markjiang7m2 原文地址:https://www.cnblogs.com/markjiang7m2/p/128371 ...

  8. Spark离线日志分析,连接Spark出现报错

    首先,我的代码是这样的 import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} object ...

  9. Spring Boot定时任务运行一段时间后自动关闭的解决办法

    用Spring Boot默认支持的 Scheduler来运行定时任务,有时在服务器运行一段时间后会自动关闭.原因:Schedule默认是单线程运行定时任务的,即使是多个不同的定时任务,默认也是单线程运 ...

  10. 《 .NET并发编程实战》一书中的节流为什么不翻译成限流

    有读者问,为什么< .NET并发编程实战>一书中的节流为什么不翻译成限流? 这个问题问得十分好!毕竟“限流”这个词名气很大,耳熟能详,知名度比“节流”大多了. 首先,节流的原词Thrott ...