单例模式

实现思路

  1. 首先,目标是什么?

    使某一个类的实例全局唯一,避免重复创建对象,节省内存资源

    同时有些对象不应该甚至不能有多个实例

  2. 那么如何做到?

    首先私有化构造方法,这样其他所有类都不能任意创建该对象的实例,除了他自己。所以这个唯一对象会是他自己调用自己的构造方法创建出来的

    根据创建时机的不同,又分为“饿汉式”和“懒汉式”

    • 饿汉式:在类初始化时就创建好,所以是private static
    • 懒汉式:在getInstance()被调用时创建

    而且自己创建的也是静态(类)实例,这样才能保证唯一

  3. 那其他类如何获取这个唯一实例呢?

    通过公有暴露的getInstance()方法,返回这个实例对象

    注意,为了避免“鸡生蛋,蛋生鸡”的问题,这个getInstance()方法也是static,可以直接通过类名.getInstance()调用,而不需要实例化对象再调用

  4. 如果多线程并发getInstance()会怎么样?

    饿汉模式没有线程安全问题,只考虑懒汉式代码:

    public LazySingleton getInstance(){
        if(lazySingleton==null) lazySingleton = new LazySingleton();
        return lazySingleton;
    }
    

    当实例还没有被实例化,此时两个线程同时getInstance(),他俩对于if(instance==null)的判断都是true,然后各自去实例化了一个实例(虽然后面的会覆盖前面的引用地址,但是确实是在堆区实例化了两个对象)

    或者是一个线程判断完,但是还没完成实例化,另一个线程进来判断了

    那么如何解决?

    最直接的就是给getInstance()方法加上synchronized关键字,但是仔细想,除了第一次实例化的时候需要同步,后面的判空和获取实例并不需要同步,同步了反而会降低效率

    那么…同步代码块?只对if语句加锁?好像好一些了,但是还不够,因为如果每个线程都要排队判断,好像跟上面同步方法差不多

    我们需要尽可能排除同步对后面的影响,对new同步?好像也不行,如果多线程判断已经结束了,那么实例化也只是排队的问题,仍然会创建多个实例

    所以最后变成了这样,两次空判断

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

    但是这里还有一点需要考虑,为什么这里加了一个volatile关键字?

    private volatile static Singleton instance;
    

    我们知道volatile关键字是用来保证多线程间对变量的可见性

    书上的说法是:

    确保当uniqueInstance变量被初始化成Singleton实例时,多个线程能正确处理uniqueInstance变量

    看网上文章说是防止实例化过程中的指令重排

    instance = new Singleton(); // 防止被排成1,3,2
    // 可以分解为以下三个步骤
    1 memory=allocate();// 分配内存 相当于c的malloc
    2 ctorInstanc(memory) //初始化对象
    3 s=memory //设置s指向刚分配的地址
    

    不甚理解,可以参考这篇文章

代码

饿汉模式

/**
 * 饿汉式的单例模式
 * @author yao 2022/12/8
 */
public class HungrySingleton {
    // 饿汉式在类加载时就实例化对象
    private static HungrySingleton hungrySingleton=new HungrySingleton();

    /**
     * 私有化构造方法,只有类本身可以创建实例
     */
    private HungrySingleton(){}

    /**
     * 公开暴露的方法可以让其他类获取到这个唯一实例
     * @return 本类的唯一实例
     */
    public static HungrySingleton getInstance(){
        return hungrySingleton;
    }
}

懒汉模式

/**
 * 懒汉模式单例
 * @author yao 2022/12/8
 */
public class LazySingleton {

    // 懒汉模式不在类初始化时实例化对象
    private static LazySingleton lazySingleton;

    /**
     * 这个写出来其实就只是为了加个private
     * 当然这么说只是因为构造方法没有参数
     */
    private LazySingleton(){}

    public LazySingleton getInstance(){
        // 只有在被调用的时候才会实例化对象,而且只会被实例化一次
        if(lazySingleton==null) lazySingleton = new LazySingleton();
        return lazySingleton;
    }
}

线程安全写法

/**
 * 线程安全且效率最高的实现
 * @author yao 2022/10/10
 */
public class Singleton {
    private volatile static Singleton instance;

    public static Singleton getInstance(){
        if(instance==null){
            synchronized (Singleton.class){
                if(instance==null) instance = new Singleton();
            }
        }
        return instance;
    }
}
posted @ 2022-12-08 11:51  YaosGHC  阅读(19)  评论(0)    收藏  举报