Java设计模式——单例模式的实现

对于单例模式的实现,在Java面试中也是会经常问到,单例模式有好多种实现方法,让我在下面给大家展开讲解

 

1.饿汉模式

class Singleton1 { // 饿汉模式
    private static Singleton1 singleton1 = new Singleton1();
    private Singleton1(){}

    public static Singleton1 getInstance(){
        return singleton1;
    }
}

在这种模式下,先定义一个static修饰(用static修饰就可以保证唯一性,保证所有类对象使用的都是这一个singleton)的singleton对象并实例化,甭管用不用,先实例化,这就是饿汉模式,

缺点就是浪费内存。但是优点是这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。

2.懒汉模式

class Singleton2 {  // 懒汉模式
    private static Singleton2 singleton2;

    private Singleton2(){
        if (singleton2 == null)
            singleton2 = new Singleton2();
    }

    public static Singleton2 getInstance(){
        return singleton2;
    }

}

懒汉模式的实现是有一个私有的构造方法,当你尝试去实例化这个类的时候,他会自动new一个singleton出来,用的时候再去实例化,这就是懒汉模式

当然这种模式有什么缺点呢?那就是线程不安全,当你对面试官回答这两种实现方法的时候,面试官肯定会继续问你,那如何保证单例模式线程安全呢?

让我们往下看

 

3.双重检查锁

class Singleton3 {  // 双重检查锁
    private static Singleton3 singleton3;

    private Singleton3(){}

    public static Singleton3 getInstance(){
        if (singleton3== null){
            synchronized (Singleton3.class){
                if (singleton3== null)
                    singleton3 = new Singleton3();
            }
        }
        return singleton3;
    }
}

首先判断singleton是否被实例化,如果没有的话,使用synchronized获取类锁,保证没有别的线程可以操作这个类,然后再去判断锁这个类的时候singleton是否被实例化,如果没有,那就new一个

 

但是这又会有一个什么问题呢?

 

假设线程A执行到了第1个if,它判断对象为空,于是线程A继续执行去初始化这个对象,但初始化是需要耗费时间的,但是这个对象的地址其实已经存在了。此时线程B也执行到了第1个if,它判断不为空,于是直接跳到后面得到了这个对象。但是,这个对象还没有被完整的初始化!得到一个没有初始化完全的对象有什么用!!

所以这时候还有一种解决办法

4.静态初始化块

class Singleton4{  

    private static class SingletonHolder
    {
        public final static Singleton4 instance = new Singleton4();
    }

    public static Singleton4 getInstance()
    {
        return SingletonHolder.instance;
    }
}

这种方法使用内部类来做到延迟加载对象,在初始化这个内部类的时候,JLS(Java Language Sepcification)会保证这个类的线程安全。这种写法最大的美在于,完全使用了Java虚拟机的机制进行同步保证,没有一个同步的关键字。