一、volatile特性:

volatile是Java虚拟机提供的轻量级的同步机制。主要有三大特性:

  • 保证可见性
  • 不保证原子性
  • 禁止指令重排序

1、保证可见性

1)代码演示

AAA线程修改变量number的值为60,main线程获取到的number值是0,就一直循环等待。

原因:int number = 0;number变量之前没有添加volatile关键字,没有可见性。添加volatile关键字,可以解决可见性问题。

public class VolatileDemo {
int number = 0; public void addTo60() {
this.number = 60;
} //volatile可以保证可见性,及时通知其他线程,主物理内存的值已经被修改
public static void main(String[] args) {
VolatileDemo volatileDemo = new VolatileDemo();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " come in");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
volatileDemo.addTo60();
System.out.println(Thread.currentThread().getName() + " update number value:" + volatileDemo.number); }, "AAA").start(); //第2个线程是main线程
while (volatileDemo.number == 0) {
//main线程就一直等待循环,直到number的值不等于0 }
System.out.println(Thread.currentThread().getName() + " mission is over, main thread number value:" + volatileDemo.number);
}
}  

2)volatile是如何来保证可见性的呢?

如果对声明了volatile的变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令。

  • 将这个变量所在缓存行的数据写回到系统内存。
  • 这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据无效。

2、不保证原子性

1)代码演示

volatile修饰number,进行number++操作,每次执行number的返回结果都不一样

public class VolatileDemo {
volatile int number = 0; public void increase() {
number++;
} public static void main(String[] args) {
VolatileDemo volatileDemo = new VolatileDemo();
for (int i = 0; i < 20; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
volatileDemo.increase();
}
}).start();
} // 默认有 main 线程和 gc 线程
while (Thread.activeCount() > 2) {
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + " finally number value:" + volatileDemo.number);
}
}

2)volatile为什么不保证原子性?

number++被拆分成3个指令:

getfield  从主内存中拿到原始值
iadd 在线程工作内存中进行加1操作
putfield 把累加后的值写回主内存
如果第二个线程在第一个线程读取旧值和写回新值期间读取number的值,
那么第二个线程就会与第一个线程看到同一个值,并执行相同值的加1操作,这也就造成了线程安全失败。

3)如何解决原子性问题

  • CAS机制:AtomicInteger number = AtomicInteger(0)
  • 锁机制:synchronized、Lock

3、禁止指令重排序

volatile的写-读与锁的释放-获取有相同的内存效果。

volatile写-读的内存语义:

当写一个volatile变量时,JMM会把线程A对应的本地内存中的共享变量值刷新到主内存。
当读一个volatile变量时,JMM会把线程B对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。

线程A写一个volatile变量,随后线程B读这个volatile变量,实质上是线程A通过主内存向线程B发送消息。
public class VolatileExample {
int a = 0; volatile boolean flag = false; public void writer() {
a = 1;
flag = true;
} public void reader() {
if (flag) {
System.out.println("resultValue:" + a);
} }
}

为了实现volatile的内存语义,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序

volatile写插入内存屏障:

volatile读插入内存屏障:

二、你在哪些地方用到过volatile

1、单例模式(双重检查锁DCL)

以下代码不一定线程安全,原因是有指令重排序的存在,某个线程执行到第一次检测,读取到的instance不为null时,instance的引用对象可能没有完成初始化
因为instance = new SingletonDemo();可以分为以下3步完成(伪代码)
memory = allocate(); //1.分配对象内存空间
instance(memory); //2.初始化对象
instance = memory; //3.设置instance指向刚分配的内存地址,此时instance!=null
步骤2和步骤3间可能会重排序
使用volatile禁止指令重排序,对volatile变量的写操作都先行发生于后面对这个变量的读操作
public class SignletonDemo {

    private static SignletonDemo instance;

    private SignletonDemo() {
System.out.println(Thread.currentThread().getName() + " 构造方法SingletonDemo");
} public static SignletonDemo getInstance() {
//第一次检测
if (instance == null) {
//同步
synchronized (SignletonDemo.class) {
if (instance == null) {
//多线程环境下可能会出现问题的地方
instance = new SignletonDemo();
}
}
}
return instance;
}
}

附:六种常见的单例模式

2、读写锁手写缓存

3、CAS JUC包中大量使用volatile

p.p1 { margin: 0; font: 15px Helvetica }
p.p1 { margin: 0; font: 15px Helvetica }
p.p1 { margin: 0; font: 15px Helvetica }
p.p1 { margin: 0; font: 15px Helvetica }
p.p1 { margin: 0; font: 15px Helvetica }
p.p1 { margin: 0; font: 15px Helvetica }
span.s1 { font: 11.5px Helvetica; color: rgba(255, 102, 0, 1) }

最新文章

  1. vue.js几行实现的简单的todo list
  2. Unity 特殊文件夹 : 位置不能随便放
  3. Java中分别取数的整数及小数部分
  4. windows 配置 apache + php
  5. Java 命令行运行参数大全
  6. linux怎么运行.SH文件
  7. zoj 3460 Missile【经典建图&amp;&amp;二分】
  8. V微软S2015下载:开展Win10/Linux/iOS多平台软件
  9. Spring4 IOC详解
  10. 关于隐式创建vue实例实现简化弹出框组件显示步骤
  11. 个人信息——头像更换(拍照或相册上传)~ 微信小程序
  12. centos 7下rabbitmq安装(转)
  13. 合并数组,改变原数组apply与不改变原数组
  14. Git的学习与使用
  15. 一些常用的排序算法(C版)
  16. background: inherit制作倒影、单行居中两行居左超过两行省略
  17. 【前端】javascript+jQuery实现旋转木马效果轮播图slider
  18. MFC OnOk(),OnCancel(),OnClose(),OnDestroy()的区别总结
  19. openal资料转贴
  20. http之请求报文request

热门文章

  1. PAUL ADAMS ARCHITECT :阿联酋和美国富人推动英国高端房地产市场
  2. Dyno-queues 分布式延迟队列 之 生产消费
  3. ElasticSearch 文档及操作
  4. WPF 关于ComboBox在前台绑定XML数据的一些方法,使用XML数据提供器 XmlDataProvider
  5. svn报错Previous operation has not finished; run &#39;cleanup&#39; if it was interrupted
  6. Linux安装jdk(两种方式)
  7. SpringBoot启动报错 Disconnected from the target VM, address: &#39;127.0.0.1:2227&#39;, transport: &#39;socket&#39;
  8. `curl -L` 解决 GitHub 的 raw.githubusercontent.com 无法连接问题
  9. C++高精度计算(大整数类)
  10. Celery:小试牛刀