单例相关

71 阅读2分钟

写一个懒汉式单例

public class A {
    
    private static volatile A instance = null;
    
    public static A getInstance() {
        if(instance == null) {
            synchronized(A.class) {
                if(instance == null) {
                    instance = new A();
                }
            } 
        }
        return instance;
    }
    
    private A(){}
}

注意点:

  1. 不要忘记将构造器写成私有
  2. 不要忘记使用volatile来修饰单例对象

为什么要用volatile修饰变量instance

volatile在这里保证了对instance变量操作时候,指令不会发生重排序。

如果不用volatile会有什么问题

对于new一个对象,从jvm层面来讲,主要会有三个步骤

  1. 在堆中为对象分配内存空间
  2. 执行对象的初始化方法
  3. 将内存空间指向其引用,返回引用

对于不加volatile修饰的变量,new对象时候,可能会存在指令优化,会先执行步骤3,再执行步骤2.执行完步骤3后,判断instance就不会等于null了,此时其他线程来调用方法getInstance时候,就可以直接拿到instance对象,这个时候步骤2初始化还没执行,此时如果拿到对象调用其内部方法时候,会出现空指针,因为其还没有初始化。

单例中的两层if判断分别起什么用

  1. 第一层判断主要用于提升性能,对于单例只有第一次需要去实例化对象时候存在多线程问题,对象实例化好后,后续每次就可以直接返回对象实例了。第一层判断就是为了保证在对象已经实例化好后,不用再每次去建立锁和释放锁的操作了,提升性能。
  2. 第二层判断主要用于多线程安全,对于两个线程同时来获取实例时候,两者都会争取锁,对于线程A争取到了锁,那么线程B此时会等待线程A释放锁,线程A实例化了对象,释放了锁,此时线程B接着执行,又会重新实例化一个对象,将先前的对象覆盖了。单例被破坏了。

synchroized中锁住的对象可以用instance吗

不可以,因为instance第一次是null,此时会直接报空指针异常。