关于我想知道懒汉模式 为什么要使用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关键字这回事

面试突击51:为什么单例一定要加 volatile?

单例模式(懒汉模式-双检锁饿汉模式静态内部类模式)-详细

单例模式(懒汉模式-双检锁饿汉模式静态内部类模式)-详细

Java 设计模式 -- 单例模式的实现(饿汉式枚举饿汉式懒汉式双检锁懒汉式内部类懒汉式)jdk 中用到单例模式的场景DCL实现单例需用volatile 修饰静态变量

Java 设计模式 -- 单例模式的实现(饿汉式枚举饿汉式懒汉式双检锁懒汉式内部类懒汉式)jdk 中用到单例模式的场景DCL实现单例需用volatile 修饰静态变量