Volatile关键字解析
2020-04-01 16:11:57来源:博客园 阅读 ()
Volatile关键字解析
volatile是Java虚拟机提供的轻量级的同步机制(“乞丐版”的synchronized)
- 保证可见性
- 不保证原子性
- 禁止指令重排
可见性
指当多个线程访问同一个变量时,如果其中一个线程修改了这个变量的值,其他线程能够立即看得到修改的值
验证可见性demo:
import java.util.concurrent.TimeUnit;
class MyData {
volatile int number = 0;
public void addTo60() {
number = 60;
}
}
public class VolatileDemo {
public static void main() {
MyData myData = new MyData();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t come in");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
myData.addTo60();
System.out.println(Thread.currentThread().getName() + "\t updated number: " + myData.number);
}, "AAA").start();
while (myData.number == 0) {}
System.out.println(Thread.currentThread().getName() + "\t mission is over");
}
}
结果:
AAA come in
main mission is over
AAA updated number: 60
不保证原子性
原子性:程序中的所有操作是不可中断的,要么全部执行成功要么全部执行失败
不保证原子性正是volatile轻量级的体现,多个线程对volatile修饰的变量进行操作时,会出现容易出现写覆盖的情况(i++)
验证不保证原子性demo:
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
class MyData {
volatile int number = 0;
public void addPlusPlus() {
number++;
}
}
public class VolatileDemo {
public static void main(String[] args) {
MyData myData = new MyData();
for (int i = 0; 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(Thread.currentThread().getName() + "\t finally number value: " + myData.number);
}
}
结果:
main finally number value: 19109
解决不保证原子性问题:Atomic
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
class MyData {
volatile int number = 0;
public void addPlusPlus() {
number++;
}
AtomicInteger atomicInteger = new AtomicInteger();
public void addAtmic() {
atomicInteger.getAndIncrement();
}
}
public class VolatileDemo {
public static void main(String[] args) {
MyData myData = new MyData();
for (int i = 0; i < 20; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
myData.addAtmic();
}
}, String.valueOf(i)).start();
}
while (Thread.activeCount() > 2) {
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + "\t finally number value: " + myData.number);
System.out.println(Thread.currentThread().getName() + "\t AtomicInteger type,finally number value: "
+ myData.atomicInteger);
}
}
结果:
main finally number value: 19746
main AtomicInteger type,finally number value: 20000
禁止指令重排
指令重排:为了提高程序运行效率,编译器可能会对输入指令进行重新排序,即程序中各个语句的执行先后顺序同代码中的顺序不一定一致。(但是它会保证单线程程序最终执行结果和代码顺序执行的结果是一致的,它忽略了数据的依赖性)
源代码 -> 编译器优化重排 -> 指令并行重排 -> 内存系统重排 -> 最终执行指令
volatile能够实现禁止指令重排的底层原理:
- 内存屏障(Memory Barrier):它是一个CPU指令。由于编译器和CPU都能够执行指令重排,如果在指令间插入一条Memory Barrier则会告诉编译器和CPU,任何指令都不能和该条Memory Barrier指令进行重排序,即通过插入内存屏障指令能够禁止在内存屏障前后的指令执行重排序
优化 - 内存屏障的另外一个作用是强制刷新各种CPU的缓存数据,因此任何CPU上的线程都能够读取到这些数据的最新版本。以上两点正好对应了volatile关键字的禁止指令重排序和内存可见性的特点
- 对volatile变量进行写操作时,会在写操作之后加入一条store屏障指令,将工作内存中的共享变量copy刷新回主内存中;对volatile变量进行读操作时,会在读操作之前加入一条load的屏障指令,从主内存中读取共享变量
应用场景:
-
高并发环境下DCL单例模式使用volatile
public class SingletonDemo { private static volatile SingletonDemo instance = null; private SingletonDemo() { System.out.println(Thread.currentThread().getName() + "我是构造方法SingletonDemo()"); } 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 = 0; i < 10; i++) { new Thread(() -> { SingletonDemo.getInstance(); }, String.valueOf(i)).start(); } } }
-
JUC包下AtomicXxx类:原子类AtomicXxx中都有一个成员变量value,该value变量被声明为volatile,保证
AtomicXxx类的内存可见性,而原子性由CAS算法&Unsafe类保证,结合这两点才能让AtomicXxx类很好地替代synchronized关键字。public class AtomicInteger extends Number implements java.io.Serializable { // ... private volatile int value; // ... }
原文链接:https://www.cnblogs.com/MessiXiaoMo3334/p/12615823.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
- JVM常见面试题解析 2020-06-11
- 深入解析ThreadLocal和ThreadLocalMap 2020-06-08
- 面试官:兄弟,说说Java的static关键字吧 2020-06-04
- dockerMesos配置项是怎么解析的?案例详解 2020-06-02
- 最新115道经典Java面试题及答案解析,快来看看你掌握了多少 2020-06-01
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash