四川省住房与城乡建设厅网站管网,旅游网站模板html,做网站公司青岛,网站制作 电子商城【版权声明】未经博主同意#xff0c;谢绝转载#xff01;#xff08;请尊重原创#xff0c;博主保留追究权#xff09; https://blog.csdn.net/m0_69908381/article/details/134430096 出自【进步*于辰的博客】 启发之作#xff1a;Java volatile关键字最全总结#xf… 【版权声明】未经博主同意谢绝转载请尊重原创博主保留追究权 https://blog.csdn.net/m0_69908381/article/details/134430096 出自【进步*于辰的博客】 启发之作Java volatile关键字最全总结原理剖析与实例讲解(简单易懂)转发。 参考笔记二P73、P74.1。 文章目录 1、关于JMM规范2、关于volatile3、关于volatile的运用4、最后 在学习
volatile关键字之前我们先了解一下JMM规范和并发编程中的三个概念。 1、关于JMM规范 什么是 J M M \color{grey}{什么是JMM} 什么是JMM JMMJava module memoryJava内存模型是一个抽象概念并不真实存在于内存。它是用于定义程序中各个变量成员变量、类变量、数组元素等的一组规范和规则指定变量的访问方式。 规定 \color{red}{规定} 规定
线程解锁之前必须将共享变量刷新回主内存线程加锁之前必须读取主内存中变量的最新值到工作空间解锁和加锁必须是同一把锁。
大家可能不解其意这就需要涉及另一个概念 线程空间 \color{green}{线程空间} 线程空间.。 什么是线程空间 \color{grey}{什么是线程空间} 什么是线程空间 程序执行JMM规范的实体是线程当线程创建时JMM会为其创建一个私有内存也称为 工作内存、本地内存或栈空间 工作内存、本地内存或栈空间 工作内存、本地内存或栈空间。JMM规定所有变量都保存在主内存线程访问变量时需为变量创建一个副本至工作内存进行操作完成后将变量值返回主内存且线程通信在主内存进行。
2、关于volatile
并发编程的三个概念 可见性 \color{green}{可见性} 可见性指线程对变量的修改其他线程可见 原子性 \color{blue}{原子性} 原子性指线程对变量的操作的整个过程不会被阻塞或分割 有序性 \color{brown}{有序性} 有序性也称为 “指令重排” \color{red}{“指令重排”} “指令重排”指程序运行时编译器基于提高性能需要以指令间的数据依赖性作为依据对指令进行重新排列。执行顺序编译器重排 → 指令并行重排 → 内存系统重排。
volatile 是什么 \color{grey}{是什么} 是什么 volatile是一种轻量级的同步机制而synchronized是一种重量级的同步机制“级”是指对变量访问的限制程度。volatile遵循JMM规范实现了可见性和有序性但不保证原子性。因此限制线程在访问由volatile修饰的变量时从主内存获取数据而不是从工作内存在数据操作完成后再刷新回主内存故在保证原子性的情况下可实现线程安全。
注如何保证原子性如程序中不存在多线程对变量进行非原子性操作举个例a是原子操作而a1不是。
volatile 的一个经典应用 \color{red}{的一个经典应用} 的一个经典应用 关于单例模式可查阅博文【关于对【单例模式_java】的理解与简述】。 从文中可知 “双重检测机制” \color{green}{“双重检测机制”} “双重检测机制”可解决“懒汉式”的线程安全问题。其实“双重同步锁”也有漏洞。 以那篇博文的示例为例
instance new Singleton();实例化分为三步1、创建实例分配内存2、实例初始化3、令instance指向此实例。其中2和3都依赖于1而2与3之间没有依赖关系故指令重排会将2与3对调原因可能是实例初始化耗时较长。因此当instance指向实例时实例可能还未初始化下一个线程就会出现并发问题暂不清楚原因用volatile禁止指令重排即可解决。
3、关于volatile的运用
学以致用才是检验学习效果最好的方法。从上文可知volatile关键字可以解决这两种情形下的线程安全问题。
多线程并发访问变量线程体中不存在非原子操作的情况弥补 双重同步锁 \color{green}{双重同步锁} 双重同步锁的漏洞。
那我们就一一测试检测一下。
1、情形一创建10个线程对同一个成员变量并发修改1万次。 示例。
volatile int a;
public static void main(String[] args) throws Exception {C c1 new C();// C 是当前类名int i 10;while (i-- 0) {new Thread(() - {int n 10000;while (n-- 0) {c1.a;}}).start();}Thread.sleep(10000);// 主线程停留10s足以保证10个子线程运行完成System.out.println(c1.a);
}最后c1.a的输出结果并不是10000010s足够10个子线程执行完成。可见并未解决线程安全问题。
2、情形二多线程并发调用newInstance()获取单例模式类实例。 实体类。
class SingleTon {private static SingleTon instance;private SingleTon() {}public static SingleTon newInstance() {if (instance null) {synchronized (SingleTon.class) {if (instance null) {instance new SingleTon();}}}return instance;}
}测试创建一万个线程并发调用newInstance()判断获取的实例是否都为单例。
ListSingleTon list new Vector();
int i 10000;
while (i-- 0) {new Thread(() - {SingleTon s1 SingleTon.getInstance();if (list.size() 0 list.indexOf(s1) -1)System.out.println(违反单例);// 未执行list.add(s1);}).start();
}
Thread.sleep(1000);
System.out.println(list.size());// 10000Vector 类线程同步故list.add(s1)也是线程同步的。 未打印“违反单例”表示list中存储的所有s1都指向同一个实例保证了“单例”说明线程安全。
不过还证明不了这是volatile的功劳因为 双重检测机制 \color{blue}{双重检测机制} 双重检测机制本身对线程安全就有很大的保证性。 于是我把10000改成了100000好吧。。。还是未打印“违反单例”看来双重同步锁真的很强大。
4、最后
大家肯定也看出来了在上面的示例中我的本意是想创建十万个线程调用newInstance()通过是否打印“违反单例”来触发 双重同步锁 \color{brown}{双重同步锁} 双重同步锁的漏洞然后用volatile声明instance来解决线程安全问题可我失败了。。。 大家也看得出来我对volatile关键字的理解还不够透彻毕竟哪有这样测试的。 因此本文的目的是为了让大家对volatile关键字有一个初步的了解我继续努力
本文完结。