设计模式之单例模式

单例模式简介

单例模式是一种简单的创建模式,使用单例模式需要注意以下几点:一是该类只能有一个实例;二是该类必须自行创建实例;三是该类必须自行向整个系统提供该实例。单例模式虽然简单,但是有很多细节需要注意,下面我们将会依次进行讲解。

懒汉式(线程不安全)

package com.pattern.demo.Singleton;

/**
 * 懒汉式 - 非线程安全
 */

public class Singleton {

    private static Singleton instance;

    /**
     * 将构造方法设为私有的
     */
    private Singleton() {}

    public static Singleton getInstance() {
        if(null == instance) {
            instance = new Singleton();
        }

        return instance;
    }
}

缺点:无法保证线程安全,当两个线程同时执行到 if(null == instance) 语句时,无法保证实例的唯一性。

懒汉式(线程安全)

package com.pattern.demo.Singleton;

/**
 * 懒汉式 - 线程安全
 */

public class Singleton {

    private static Singleton instance;

    /**
     * 将构造方法设为私有的
     */
    private Singleton() {}

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

        return instance;
    }
}

缺点:对整个静态工厂方法进行加锁,效率较低,当有多个线程同时获取实例对象时,将会进行线程等待。

饿汉式

package com.pattern.demo.Singleton;

/**
 * 饿汉式
 */

public class Singleton {

    private static Singleton instance = new Singleton();

    /**
     * 将构造方法设为私有的
     */
    private Singleton() {}

    public static Singleton getInstance() {
        return instance;
    }
}

缺点:类加载时就会创建实例对象,无法做到懒加载,容易产生垃圾对象,浪费内存。

双检锁式

package com.pattern.demo.Singleton;

/**
 * 双检锁式
 */

public class Singleton {

    private static Singleton instance;

    /**
     * 将构造方法设为私有的
     */
    private Singleton() {}

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

优点:保证了线程安全,并且是懒加载的,避免了内存浪费。
缺点:这种方式表面上看没什么问题,但是并非绝对安全,编译器将代码编译成JVM指令时可能会涉及到指令重排的问题。在多线程的环境下,指令重排可能造成不安全的结果。举个例子,代码中 instance = new Singleton(); 语句会被编译器编译成如下JVM指令:

memory=allocate(); //1:分配对象内存空间
ctorInstance(memory); //2:初始化对象
instance=memory; //3:设置instance指向刚才分配的内存地址

这些指令的顺序可能经过JVM和CPU的优化后变为如下顺序:

memory=allocate(); //1:分配对象内存空间
instance=memory; //3:设置instance指向刚才分配的内存地址
ctorInstance(memory); //2:初始化对象

当线程A执行完1、3步骤时,instance对象并没有完成初始化,但是已经不是指向null了。此时如果线程B抢占了CPU资源,执行 if(null == instance) 语句时,返回false,从而返回了一个没有初始化完成的instance对象。

双检锁式(升级版)

要解决上面双检锁式遇到的问题,可以使用volatile修饰符进行修饰。volatile用以声明变量的值可能随时会被别的线程修改,使用volatile修饰的变量会强制将修改的值立即写入主存,主存中值的更新会使缓存中的值失效(非volatile变量不具备这样的特性,非volatile变量的值会被缓存,线程A更新了这个值,线程B读取这个变量的值时可能读到的并不是是线程A更新后的值)。volatile会禁止指令重排。

package com.pattern.demo.Singleton;

/**
 * 双检锁式 - 使用volatile修饰符
 */

public class Singleton {

    private volatile static Singleton instance;

    /**
     * 将构造方法设为私有的
     */
    private Singleton() {}

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

登记式/静态内部类式

package com.pattern.demo.Singleton;

/**
 * 登记式/静态内部类式
 */

public class Singleton {
    
    /**
     * 将构造方法设为私有的
     */
    private Singleton() {}
    
    private static class LazyHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return LazyHolder.INSTANCE;
    }
}

优点:该种方式利用了ClassLoader的加载机制来实现懒加载,并且保证了线程安全。
缺点:和上面其他方式一样,都无法防止通过反射来重复构建对象。

//通过反射打破单例模式的约束
Constructor constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton singleton1 = (Singleton) constructor.newInstance();
Singleton singleton2 = (Singleton) constructor.newInstance();

枚举

package com.pattern.demo.Singleton;

/**
 * 枚举实现单例
 */

public enum SingletonEnum {
    INSTANCE;

    public void doSomething() {

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

成汐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值