关于我想知道懒汉模式 为什么要使用volatile关键字这回事
Posted weixin_43063239
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于我想知道懒汉模式 为什么要使用volatile关键字这回事相关的知识,希望对你有一定的参考价值。
今天发现 项目中有些懒汉模式 貌似有线程安全问题
eg: 大家写懒汉模式是不是都是这样写的呢
public class VolatileTest {
private static VolatileTest volatileTest = null;
public static VolatileTest of(){
if(null == volatileTest){
volatileTest = new VolatileTest();
}
return volatileTest;
}
}
此时会发现如果of再未被调用过,现在多个线程同时调用of方法时,会出现线程安全的问题
(判断是否可能出现多线程的条件:1是否存在多线程。2是否存在共享变量)
怎么解决线程安全问题?
加锁!
eg:现在锁加好了
public class VolatileTest {
private static VolatileTest volatileTest = null;
public static VolatileTest of(){
synchronized (VolatileTest.class){
if(null == volatileTest){
volatileTest = new VolatileTest();
}
}
return volatileTest;
}
}
细节1: 这里锁用的VolatileTest.class,锁需要保证在线程中的唯一性。而class是类的模版对象。在jvm加载(生命周期:加载,链接,初始化,使用,卸载)类时 会在内存(堆)中创建唯一一个关于这个类的class对象也就是 VolatileTest.class。
提问:这样写线程是安全了 但是 没有效率。当volatileTest创建好了之后 每次获取都要走锁机制,获取对象都要排队
如何保证 创建时加锁,创建好了就不需要锁了
加判断!
eg: 大家熟知的双检锁来了
public class VolatileTest {
private static VolatileTest volatileTest = null;
public static VolatileTest of(){
if(null == volatileTest){
synchronized (VolatileTest.class){
if(null == volatileTest){
volatileTest = new VolatileTest();
}
}
}
return volatileTest;
}
}
当volatileTest对象不为null时直接跳过锁返回
好像还有问题!
对象创建大致分三个步骤 1分配内存,2初始化,3返回
指令重排!
也就是说第一个线程创建对象时可能会出现 1.分配内存,3返回,2初始化
这个样依然有很小的概率会出现线程安全问题
加 volatile修饰!
eg:
public class VolatileTest {
private static volatile VolatileTest volatileTest = null;
public static VolatileTest of(){
if(null == volatileTest){
synchronized (VolatileTest.class){
if(null == volatileTest){
volatileTest = new VolatileTest();
}
}
}
return volatileTest;
}
}
好了,到这里基本解决了懒汉模式线程安全问题
可是我想看字节码上 加volatile和不加volatile的区别
于是。。。
javac 将java文件编程成class文件
javap -v 反编译字节码
结果。。。
加不加 反编译的结果都一样
既然反编译的结果一样 是不是应该去class的16进制文件上找不同?
打开notepad++ 打开class文件 乱码。。。
百度 notepad++ 如何打开16进制文件 安装插件 HEX-Editor
安装失败T。T 我估计是网络问题。
byt 没有工具自己不能造吗!
开动!
利用io流读取class 文件 再将字节流转成16进制打印
eg:读取16进制文件工具类
public class HexRead {
public static void main(String[] args) {
File file = new File("src/test/java/jvm/designmode/VolatileTest.class");
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(file);
byte[] bytes = new byte[1024];
int lne = 0;
while ((lne=fileInputStream.read(bytes))!=-1){
for(int i = 1 ; i < lne+1; i++){
String s1 = Integer.toHexString(bytes[i-1] & 0xF);
String s0 = Integer.toHexString((bytes[i-1]>>4) & 0xF);
System.out.print(s0);
System.out.print(s1);
System.out.print(",");
if((i&0xF) == 0){
System.out.println();
}
}
// System.out.println(Arrays.toString(bytes));
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fileInputStream!=null){
fileInputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
看到cafe babe 打印出来简直美滋滋
现在我获得了一个读取16进制文件的工具类
这里就可以看到在字节码的16进制文件中 加volatile和不加volatile的区别 4和0
不禁感叹jvm的强大以及恐怖的节省内存的方式
以上是关于关于我想知道懒汉模式 为什么要使用volatile关键字这回事的主要内容,如果未能解决你的问题,请参考以下文章
关于我想知道懒汉模式 为什么要使用volatile关键字这回事
Java 设计模式 -- 单例模式的实现(饿汉式枚举饿汉式懒汉式双检锁懒汉式内部类懒汉式)jdk 中用到单例模式的场景DCL实现单例需用volatile 修饰静态变量
Java 设计模式 -- 单例模式的实现(饿汉式枚举饿汉式懒汉式双检锁懒汉式内部类懒汉式)jdk 中用到单例模式的场景DCL实现单例需用volatile 修饰静态变量