volatile是java虚拟机提供的轻量级的同步机制:

1.保证可见性:线程之间可见性(及时通知)
2.不保证原子性
3.禁止指令重排

先了解一下jvm同步
由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(或者称为栈空间),工作内存是每个线程的私有数据区域,而java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝到自己的栈空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量,各个线程中的工作内存中存储着主内存中的变量副本拷贝,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成。

一、volatile的可见性demo验证

一、没有加volatile

package Volatile;import java.util.concurrent.TimeUnit;/*** volatile的可见性* demo*/
class MyData{int number = 0;public void addTO60(){this.number = 60;}
}public class demo {public static void main(String[] args) {MyData myData = new MyData();new Thread(() -> {System.out.println(Thread.currentThread().getName() + "come in");//让线程等待3stry {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}myData.addTO60();System.out.println(Thread.currentThread().getName() + "updated number value:" + myData.number);},"AAA").start();//第二个线程是main线程while(myData.number == 0){//循环等待}System.out.println(Thread.currentThread().getName());}
}

运行结果可以看出来会卡在while循环处

二、加上volatile后

/*** volatile的可见性* demo*/
class MyData{volatile int number = 0;public void addTO60(){this.number = 60;}
}public class demo {public static void main(String[] args) {MyData myData = new MyData();new Thread(() -> {System.out.println(Thread.currentThread().getName() + "come in");//让线程等待3stry {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}myData.addTO60();System.out.println(Thread.currentThread().getName() + "updated number value:" + myData.number);},"AAA").start();//第二个线程是main线程while(myData.number == 0){//循环等待}System.out.println(Thread.currentThread().getName()+"mission is over;main get number value:"+myData.number);}
}

结果:

结果可以看出,当其他线程修改了主内存空间的值时,加上了volatile主内存空间的值改变后会及时通知其他线程主物理内存的值被修改。

二:不保证原子性demo验证

下面代码:20个线程,每个线程进行1000次number++,理论上结果是两万,实际运行:

    public static void main(String[] args) {MyData myData = new MyData();for (int i = 1; i <= 20; i++) {new Thread(() -> {for (int j = 0; j <1000 ; j++) {myData.addPlusPlus();}},String.valueOf(i)).start();}while(Thread.activeCount()>2){Thread.yield();}System.out.println(myData.number);}

结果并不是两万:所以说不能保证原子性,不能保证结果一致性,存在线程安全问题

解决办法:

1.synchronized(有点小题大做)
synchronized public void addPlusPlus() {this.number++;
}
2.使用AtomicInteger
class MyData {volatile int number = 0;public void addTO60() {this.number = 60;}public void addPlusPlus() {this.number++;}AtomicInteger atomicInteger = new AtomicInteger();public void atomicPlusPlus(){atomicInteger.getAndIncrement();}
}
public class demo {public static void main(String[] args) {MyData myData = new MyData();for (int i = 1; i <= 20; i++) {new Thread(() -> {for (int j = 0; j <1000 ; j++) {myData.addPlusPlus();myData.atomicPlusPlus();}},String.valueOf(i)).start();}while(Thread.activeCount()>2){Thread.yield();}System.out.println(myData.number);System.out.println(myData.atomicInteger);}

就是用AtomicInteger来代替number,用getAndIncrement来代替number++(Atomic相关内容可以看API了解方法怎么用。)就可以保证原子性。

三、禁止指令重排

理解这里需要了解一点编译器,编译器在编译时,会有个优化指令重排,在多线程下指令重排会造成线程安全问题,最终一致性无法保证

多线程环境中线程的交替执行,由于编译器优化重排的存在,两个线程中使用的变量能否保证一致性是无法确定的,结果无法预测。

例:单例模式为例(在多线程下,普通的单例模式并不适用)

class SingletonDemo{private static SingletonDemo instance = null;private SingletonDemo(){System.out.println(Thread.currentThread().getName()+"\t"+"调用构造方法SingletonDemo");}public static SingletonDemo getInstance(){if (instance == null){instance = new SingletonDemo();}return instance;}
}
public class demo1 {public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(()->{SingletonDemo.getInstance();}).start();}}
}

结果:可以看到并不是只会创建一个对象

使用dlc(Double Check Lock双端检索机制),代码如下

class SingletonDemo{private static SingletonDemo instance = null;private SingletonDemo(){System.out.println(Thread.currentThread().getName()+"\t"+"调用构造方法SingletonDemo");}//    public static SingletonDemo getInstance(){//        if (instance == null){//            instance = new SingletonDemo();
//        }
//        return instance;
//    }//使用dlc双端检索机制public static SingletonDemo getInstance(){if (instance == null){synchronized(SingletonDemo.class){if (instance == null){instance = new SingletonDemo();}}}return instance;}
}
public class demo1 {public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(()->{SingletonDemo.getInstance();}).start();}}
}

结果:

那么这样就可以了吗?

不行,因为编译器会进行指令重排,instance = new SingletonDemo();编译器会拆分成三步,
1.memory = allocate(); //分配对象内存空间
2.instance(memory);//初始化对象
3.instance = memory; //设置instance指向刚分配的内存地址,此时instace!=null

所以可能出现,另一个线程进入了第一个if (instance == null){时,instance的引用对象还未初始化完成,所以要加入volatile来禁止指令重排

package Volatile;class SingletonDemo{private static volatile SingletonDemo instance = null;private SingletonDemo(){System.out.println(Thread.currentThread().getName()+"\t"+"调用构造方法SingletonDemo");}//    public static SingletonDemo getInstance(){//        if (instance == null){//            instance = new SingletonDemo();
//        }
//        return instance;
//    }//使用dlc双端检索机制public static SingletonDemo getInstance(){if (instance == null){synchronized(SingletonDemo.class){if (instance == null){instance = new SingletonDemo();}}}return instance;}
}
public class demo1 {public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(()->{SingletonDemo.getInstance();}).start();}}
}

volatile学习(可见性,不保证原子性,禁止指令重排(双端检索机制))相关推荐

  1. Volatile:可见性保证+禁止指令重排

    Volatile 1.可见性保证 1.1 何为可见性 1.2 JAVA内存模型 1.3 voletile的实现原理 1.4.synchronized 关键字和 volatile 关键字的区别 2.禁止 ...

  2. 两个例子详解并发编程的可见性问题和有序性问题,通过volatile保证可见性和有序性以及volatile的底层原理——缓存一致性协议MESI和内存屏障禁止指令重排

    1. 并发编程的可见性问题 2. 并发编程的有序性问题 3. 使用volatile关键字解决可见性问题 4. 可见性问题的本质--缓存不一致 因为cpu执行速度很快,但是内存执行速度相对于CPU很慢, ...

  3. Java面试之Synchronized无法禁止指令重排却能保证有序性

    为什么Synchronized无法禁止指令重排,却能保证有序性 前言 首先我们要分析下这道题,这简单的一个问题,其实里面还是包含了很多信息的,要想回答好这个问题,面试者至少要知道一下概念: Java内 ...

  4. Volatile禁止指令重排

    Volatile禁止指令重排 计算机在执行程序时,为了提高性能,编译器和处理器常常会对指令重排,一般分为以下三种: 源代码 -> 编译器优化的重排 -> 指令并行的重排 -> 内存系 ...

  5. Volatile-3.禁止指令重排

    volatile禁止指令重排 JMM要求有序性 计算机在执行程序时,为了提高性能,编译器和处理器常常会做指令重排,一把分为以下3种 单线程环境里面确保程序最终执行结果和代码顺序执行的结果一致.(单线程 ...

  6. 一道题决定去留:为什么synchronized无法禁止指令重排,却能保证有序性?

    前几天有一位读者找我问一个问题,说是这道题可能影响了他接下来3年的技术成长. 据说这位读者前面的很多问题会的都还可以,属于那种可过可不过的类型的,面试官出了最后一道题,就是回答的满意就可以给Offer ...

  7. 一道大题决定去留:为什么synchronized无法禁止指令重排,却能保证有序性?

    △Hollis, 一个对Coding有着独特追求的人△ 这是Hollis的第 253篇原创分享 作者 l Hollis 来源 l Java之道(ID:javaways) 前几天有一位读者找我问一个问题 ...

  8. 关于学习Python的一点学习总结(54->集合->堆->双端队列)

    集合,堆,双端队列 再谈集合set:集合是由内置类set实现的 >>> set(range(10)){0, 1, 2, 3, 4, 5, 6, 7, 8, 9} 可使用序列(或其他可 ...

  9. volatile实现禁止指令重排底层操作原理

最新文章

  1. centos7 mysql 5.6.38_centos7.4 安装mysql 5.6.38
  2. Java Web项目_order下载、运行
  3. SpringBoot整合kafka之kafka分区实战
  4. 付出与收获对等的自然法则,执行力离开去做马上去做
  5. Kaleidoscope for Mac(文件和图像比较工具)
  6. CentOS下双网卡绑定-bond0
  7. 数据中心交换机芯片学习总结
  8. 网络开盘选房微信抢房软件下载及使用教程
  9. ftp服务器设置上文件大小,ftp服务器文件上传大小设置
  10. Padavan编译技巧
  11. 变频器,变频调速操作控制,QY-TS02
  12. 安卓手机的指纹存储在手机内部有没有可能被窃取?
  13. Ubuntu红外相机SDK/驱动安装(optris PI 400i / PI 450i)
  14. 液压电磁换向阀DG4V-5-24AJ-M-U-H6-20
  15. 分享一个手写汉字数据集
  16. linux上erlang编译安装手记
  17. GIT如何设置只提交文件夹或者目录,而忽略内容?
  18. 局域网、广域网、城域网的介绍,
  19. Silverlight5 做的打印用针式打印机打印时,为什么非常不清楚?
  20. 可移除 否_Cell子刊:还在为脱发烦恼?最新研究表明微调毛囊干细胞代谢,可治疗脱发...

热门文章

  1. iOS项目开发— CoreLocation的定位服务和地理编码与发编码实现
  2. 310. Minimum Height Trees
  3. linux c 读取摄像头,Linux下onvif客户端获取ipc摄像头 获取能力:GetCapabilities
  4. 1668智能下数教程视频_你需要的教程合集更新
  5. python中parse是什么_Python中optparse模块使用浅析
  6. 只读属性怎么来的初值_设置Mysql表只读,关键时刻或许能救你一命
  7. 街上第一台电子计算机是,南京信息工程大学滨江学院2009级《计算机基础》(文科)a试卷(含答案)【最新】.doc...
  8. jetty java 禁用目录列表_java – 如何禁用Jetty的WebAppContext目录列表?
  9. 图形基本变换c语言代码,图形变换-C语言课程设计.doc
  10. asterisk for mipsel