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. 多线程环境中线程交替执行,由于编译器优化重排的存在,两个线程使用的变量能否保持一致性是无法确定的,结果无法预测

代码

保证内存可见性

  1. package com.raicho.mianshi.myvolatile;
  2.  
  3. public class MyVolatileVisibility {
  4.  
  5. // private int i;
  6. private volatile int i;
  7.  
  8. public void changeI(int i) {
  9. this.i = i;
  10. }
  11.  
  12. public static void main(String[] args) {
  13. // System.out.println("没有加volatile关键字");
  14. MyVolatileVisibility myVolatile = new MyVolatileVisibility();
  15.  
  16. new Thread(() -> {
  17.  
  18. try {
  19. Thread.sleep(1000);
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. }
  23.  
  24. myVolatile.changeI(1);
  25. System.out.println(Thread.currentThread().getName()+"修改了i="+myVolatile.i);
  26. },"线程1 ").start();
  27.  
  28. System.out.println( Thread.currentThread().getName()+"访问 i = "+ myVolatile.i);
  29. while (myVolatile.i == 0) {
  30.  
  31. }
  32.  
  33. }
  34.  
  35. }

当没有加volatile关键字时,当主线程先访问了i值为0,后线程1再进行修改i=1,回到mian线程,并不能察觉到i的值修改为1,然而一直在while循环不能结束,加上volatile关键字就能够检测到其他线程已经将i的值修改为1,结束程序。

不保证原子性

  1. package com.raicho.mianshi.myvolatile;
  2.  
  3. public class VolatileAtomicity {
  4. volatile int number = 0;
  5.  
  6. public void addNum(){
  7. number++;
  8. }
  9.  
  10. public static void main(String[] args) {
  11. VolatileAtomicity va = new VolatileAtomicity();
  12.  
  13. for (int i = 0; i < 20; i++) {
  14. new Thread(()->{
  15. for (int j = 0; j < 1000; j++) {
  16. va.addNum();
  17. }
  18. },String.valueOf(i)).start();
  19. }
  20.  
  21. //等等20条线程完成
  22. while (Thread.activeCount() >2){
  23. Thread.yield();
  24. }
  25.  
  26. System.out.println(Thread.currentThread().getName()+" number = "+va.number);
  27. }
  28. }

通过代码验证并最终number并不能达到20000,证明volatile并不保证原子性操作

解决方案

  1. 在addNum()方法上加锁synchronized关键字,肯定是可以解决的,但是synchronized加锁太重了,严重降低效率
  2. 使用AtomicInteger类
  1. package com.raicho.mianshi.myvolatile;
  2.  
  3. import java.util.concurrent.atomic.AtomicInteger;
  4.  
  5. public class VolatileAtomicity {
  6. volatile int number = 0;
  7.  
  8. public void addNum(){
  9. number++;
  10. }
  11.  
  12. AtomicInteger atomicInteger = new AtomicInteger();
  13.  
  14. public void addNumAtomicInteger(){
  15. atomicInteger.getAndIncrement();
  16. }
  17.  
  18. public static void main(String[] args) {
  19. VolatileAtomicity va = new VolatileAtomicity();
  20.  
  21. for (int i = 0; i < 20; i++) {
  22. new Thread(()->{
  23. for (int j = 0; j < 1000; j++) {
  24. //va.addNum();
  25. va.addNumAtomicInteger();
  26. }
  27. },String.valueOf(i)).start();
  28. }
  29.  
  30. //等等20条线程完成
  31. while (Thread.activeCount() >2){
  32. Thread.yield();
  33. }
  34.  
  35. // System.out.println(Thread.currentThread().getName()+" number = "+va.number);
  36. System.out.println(Thread.currentThread().getName()+" number = "+va.atomicInteger);
  37. }
  38. }

禁止指令重排序

  1. public void mySort(){
  2. int x=11;//语句1
  3. int y=12;//语句2
  4. x=x+5;//语句3
  5. y=x*x;//语句4
  6. }

重新排序后可能会变为
1234
2134
1324
问题:
请问语句4 可以重排后变成第一条码?
存在数据的依赖性 没办法排到第一个

单例模式中使用双重检测机制

  1. public class SingletonDemo {
  2.  
  3. private static volatile SingletonDemo instance=null;
  4. private SingletonDemo(){
  5. System.out.println(Thread.currentThread().getName()+"\t 构造方法");
  6. }
  7.  
  8. /**
  9. * 双重检测机制
  10. * @return
  11. */
  12. public static SingletonDemo getInstance(){
  13. if(instance==null){
  14. synchronized (SingletonDemo.class){
  15. if(instance==null){
  16. instance=new SingletonDemo();
  17. }
  18. }
  19. }
  20. return instance;
  21. }
  22.  
  23. public static void main(String[] args) {
  24. for (int i = 1; i <=10; i++) {
  25. new Thread(() ->{
  26. SingletonDemo.getInstance();
  27. },String.valueOf(i)).start();
  28. }
  29. }
  30. }

在多线程下,不加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. 标准库模块time,datetime

    在Python中,通常有这几种方式来表示时间: 1)时间戳 2)格式化的时间字符串 3)元组(struct_time)共九个元素. 由于Python的time模块实现主要调用C库,所以各个平台可能有所 ...

  2. 利用POI工具读取word文档并将数据存储到sqlserver数据库中

    今天实现了利用POI工具读取word文档,并将数据存储到sql数据库中,代码如下: package word; import java.io.File; import java.io.FileInpu ...

  3. CCF NOI1039 2的n次方

    问题链接:CCF NOI1039 2的n次方. 时间限制: 1000 ms  空间限制: 262144 KB 题目描述 对于任意给定的n,计算2的n次方. 输入 输入整数n. 输出 输出2的n次方的值 ...

  4. How to get binary string from ArrayBuffer?

    https://stackoverflow.com/questions/16363419/how-to-get-binary-string-from-arraybuffer https://stack ...

  5. Codeforce-CodeCraft-20 (Div. 2)-B. String Modification (找规律+模拟)

    Vasya has a string s of length n. He decides to make the following modification to the string: Pick ...

  6. Python+wxpy 实现微信消息轰炸

    需要导入wxpy,在终端中输入以下命令即可 pip install wxpy 如果没有pip先安装pip,安装好了的直接输入命令即可,安装好了但是显示没有安装的可能是没有将pip添加到PATH中,需要 ...

  7. RocketMQ搭建全过程

    RocketMQ下载地址:https://mirrors.tuna.tsinghua.edu.cn/apache/rocketmq/4.3.0/rocketmq-all-4.3.0-bin-relea ...

  8. Datasource Server returns invalid timezone问题

    今天在学习一个项目的时候,idea中的datasource没有配置,后来发现mysql没有连接,于是下载了最新版的jdbc.jar包,连接mysql完成后,想test一下mysql connect,结 ...

  9. JDBC10 Blob二进制对象

    //将图片输入到数据库中 // String sql="insert into t_user2 (username,headImg) values (?,?)"; // ps=co ...

  10. [hdu1085]生成函数

    题意:给a个1.b个2.c个5,求不能构成最小的数 思路: 先求1能构成的所有数,2能构成的所有数,5能构成的所有数,它们的方法数显然都是1,现在考虑把3者结合在一起,由于结果为和的形式,而又是循环加 ...