设计模式之单例设计模式

设计模式相当于武功秘籍中的内功心法,学习设计模式,有助于我们在程序设计和开发中有更深程度的思考,在我们程序生涯中有着重要的意义。本文通过简单的描述来讲一下23种设计模式中的单例模式。

1、单例模式

保证一个类只有一个实例,并提供一个访问该实例的全局访问点

1.1、常见的场景

1、windows的任务管理器
2、Spring中的Bean
3、数据库连接池的设计

1.2、 优点:

单例模式只产生一个实例,减少了系统性能开销。如对象的产生需要较多的资源时,如读取配置,产生其他依赖对象时,可以在应用系统启动时产生一个单例对象,然后存在内存中。

1.3、 常见的五种单例模式实现方式

  • 1、饿汉式
    线程安全,调用效率高,但是不能延迟加载
  • 2、饱汉式
    线程安全,调用效率不高,可以延迟加载
  • 3、双重检查锁式
    由于JVM底层内部模型原因,偶尔会出问题,不建议使用
  • 4、静态内部类
    线程安全 调用效率高 实现了延时加载
  • 5、枚举单例
    线程安全,调用效率高,不能延迟加载
1.3.1、饿汉式
/**
 * 饿汉式单例模式
 * 线程安全,调用效率高,但是不能延迟加载
 * @author tqq
 * @date 2021-04-26-0:07
 */
public class SingletonDemo1 {
    //类初始化时,立即加载这个对象(没有延迟加载的优势),加载类时,天然是线程安全的
    private static SingletonDemo1 instance = new SingletonDemo1();

    private SingletonDemo1(){ //私有化构造器

    }
    //方法没有同步,调用效率高
    public static SingletonDemo1 getInstance(){
        return instance;
    }
}

注:
1、 static变量会在类装载时初始化,不会涉及到多个线程访问该对象;虚拟机只装载一次类,不会涉及到并发问题,可以省略synchronized关键字
2、 如果只装载本类,getInstance()不调用会造成资源浪费的问题

类初始化的时候就创建的好处:
1、如果初始化较长在程序启动的时候就完成初始化,就可以避免在程序运行的过程中初始化可能导致的性能问题。(如,在显影客户端接口请求时做初始化操作,可能导致相应时间过长,甚至超时)
2、如果实例占用资源多,我们希望有问题提前暴露,在程序启动时就触发报错,我们可以提前去修复,避免在程序运行一段时间后,突然因为因为初始化实例占用资源过多,影响系统的可用性。

1.3.2、饱汉式
/**
 * 饱汉式单例模式
 * 线程安全,调用效率不高,可以延迟加载
 * @author tqq
 * @date 2021-04-26-0:07
 */
public class SingletonDemo2 {
    //类初始化时,不创建这个对象(延迟加载,真正用的时候再创建)
    private static SingletonDemo2 instance;

    private SingletonDemo2(){ //私有化构造器

    }
    //方法同步,调用效率低
    public static synchronized SingletonDemo2 getInstance(){
        if(instance==null){
            instance = new SingletonDemo2();
        }
        return instance;
    }
}

我们给getInstance()的方法加了一把锁,导致这个函数的并发度很低
如果这个单例类偶尔被调用,这种实现方式还可以接受;如果频繁地用到,那么频繁加锁、释放锁和并发度低等问题,会导致性能瓶颈。

1.3.3、双重检查锁式
package 单例设计模式;

/**
 * 双重检查锁单例模式
 * 由于JVM底层内部模型原因,偶尔会出问题,不建议使用
 * @author tqq
 * @date 2021-04-26-0:07
 */
public class SingletonDemo3 {
    private static SingletonDemo3 instance = null;

    public static SingletonDemo3 getInstance() {
        if (instance == null) {
            SingletonDemo3 sc;
            synchronized (SingletonDemo3.class) {
                sc = instance;
                if (sc == null) { //这次判断是防止有并发问题
                    synchronized (SingletonDemo3.class) {
                        if(sc == null) {
                            sc = new SingletonDemo3();
                        }
                    }
                    instance = sc;
                }
            }
        }
        return instance;
    }

    private SingletonDemo3() {

    }
}
public class SingletonDemo3 {
//volatile 保证可见性,可加可不加
private volatile static SingletonDemo3   instance  = null;

private SigletonDemo3 (){};

public static  SingletonDemo3 getInstance(){
	if(instance == null){
		synchronized (SingletonDemo3.class){
			if(instance == null){
				instance = new SingletonDemo3()
			}
		}
	}
	return instance;
}
}
1.3.4、静态内部类
/**
 * 静态内部类实现方式(也是一种懒加载方式)
 * 线程安全 调用效率高  实现了延时加载
 * @author tqq
 * @date 2021-04-26-9:59
 */
public class SingletonDemo4 {
    //外部没有static属性,不会像饿汉式那样立即加载对象
    //只有真正调用getInstance()才会加载静态内部类。加载类时是线程安全的。instance是static final类型,保证了内存中只有
        //这样的一个实例存在,而且只能被赋值一次,从而保证了线程的安全性,
    //兼备了并发高效调用和延迟加载的优势
    private static class SingletonClassInstance{
        private static final SingletonDemo4 instance = new SingletonDemo4();
    }
    public static SingletonDemo4 getInstance(){
        return SingletonClassInstance.instance;
    }
    private SingletonDemo4(){

    }
}
1.3.5、枚举
/**
 * 枚举单例模式  (没有延时加载)
 * 线程安全,调用效率高
 * @author tqq
 * @date 2021-04-26-10:11
 */
public enum SingletonDemo5 {
   //这个枚举元素,本身就是个单例
    INSTANCE;
    public  void singletonOperation(){
        //功能处理
    }

}
1.3.6 测试单例模式
/**
 * 测试单例
 * @author tqq
 * @date 2021-04-26-10:15
 */
public class Client {
    public static void main(String[] args) {
        SingletonDemo1 s1 = SingletonDemo1.getInstance();
        SingletonDemo1 s2 = SingletonDemo1.getInstance();
        SingletonDemo2 s3 = SingletonDemo2.getInstance();
        SingletonDemo2 s4 = SingletonDemo2.getInstance();
        SingletonDemo4 s5 = SingletonDemo4.getInstance();
        SingletonDemo4 s6 = SingletonDemo4.getInstance();
        SingletonDemo5 s7 = SingletonDemo5.INSTANCE;
        SingletonDemo5 s8 = SingletonDemo5.INSTANCE;
        System.out.println(s1==s2); //true
        System.out.println(s3==s4); //true
        System.out.println(s5==s6); //true
        System.out.println(s7==s8); //true
    }
}
总结

1、当单例对象占用资源少,不需要延迟加载:
枚举式好于饿汉式
2、当单例对象占用资源大 需要延迟加载
静态内部类 好于饱汉式

程序源码
https://gitee.com/hellotqq/tqq-javase-study

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值