Java设计模式之单例模式

国际惯例,扯闲话

第一次写博客,新手一名,轻喷~~

核心作用

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

应用场景

  1. 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,都new一个对象去读取。
  2. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。
  3. 在servlet编程中,每个servlet也是单例

单例模式的优点

由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。

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

  1. 饿汉式(线程安全,调用效率高,但是,不能延时加载)
  2. 懒汉式(线程安全,调用效率不高,但是,可以延时加载)
  3. 双重检测锁式(由于JVM内部模型原因,偶尔会出现问题,不建议使用)
  4. 静态内部类式(线程安全,调用效率高,但是,也可以延时加载,是不是很完美?差不多就是用它了!)
  5. 枚举单例(线程安全,调用效率高,但是不能延时加载)

饿汉式实现(单例对象立即加载)

public class SingletonDemo {

    //1、提供一个静态属性。类加载时,立即加载该对象,天然的是线程安全的
    private static /*final*/ SingletonDemo singletonDemo = new SingletonDemo();

    //2、私有化构造器(这样别人就访问不了了,但是反射好像可以...)
    private SingletonDemo() {

    }

    //3、提供一个开放的静态方法,将该实例返回。方法没有同步,调用效率高
    public static /*synchronized*/ SingletonDemo getInstance() {
        return singletonDemo;
    }
    
}

static变量在类装载时初始化,此时也不会发生多个线程对象访问该对象的问题。 虚拟机保证只会装一次该类,肯定不会发生并发访问的问题。所以synchronized可以省略。

问题:
如果只是加载该类,而不是调用getInstance(),甚至永远没有调用,则会造成资源浪费!

懒汉式实现(单例对象延迟加载)

public class SingletonDemo02 {

    //1、声明对象。只做声明,类初始化时不加载这个对象,实现延迟加载,真正用的时候才创建
    private static SingletonDemo02 instance;

    //2、将构造器私有化
    private SingletonDemo02() {

    }

    //3、提供一个开放方法,返回实例对象。方法同步,调用效率低。
    public static synchronized SingletonDemo02 getInstance() {
        if (instance == null) {
            instance = new SingletonDemo02();
        }
        return instance;
    }
}

要点:
延迟加载,懒加载,即真正用的时候才加载。

问题:
资源利用率变高了,但是,每次调用getInstance()方法都需要同步,并发效率比较低。

双重检测锁实现

public class SingletonDemo03 {

    //1、声明实例对象
    private static SingletonDemo03 instance = null;

    //2、私有化构造器
    private SingletonDemo03() {

    }

    //3、提供开放的获取实例对象方法
    public static SingletonDemo03 getInstance() {
        if (instance == null) {
            SingletonDemo03 s;
            synchronized (SingletonDemo03.class) {
                s = instance;
                if (s == null) {
                    synchronized (SingletonDemo03.class) {
                        if (s == null) {
                            s = new SingletonDemo03();
                        }
                    }
                    instance = s;
                }
            }
        }
        return instance;
    }
}

将方法同步内容移动到if内,提高了执行的效率,不必每次都同步,只有第一次才同步,对象创建了之后就没必要了

问题:
由于编译器优化,JVM底层内部模型的原因,偶尔会出现问题,不建议使用

静态内部类实现(也是一种懒加载)

public class SingletonDemo04 {

    //1、创建私有的静态内部类,在该类中new出实例对象
    private static class SingletonInstanceClass{
        private static final SingletonDemo04 instance = new SingletonDemo04();
    }

    //2、构造器私有化
    private SingletonDemo04() {

    }

    //3、提供外部可访问的方法将实例对象返回
    public static SingletonDemo04 getInstance() {
        return SingletonInstanceClass.instance;
    }

}

要点:

  1. 外部没有static属性,则不会像饿汉式那样立即加载对象
  2. 只有真正调用getInstance()方法,才会加载静态内部类。加载类时是线程安全的。instance是static final类型,保证内存中只有一个实例存在,且只能赋值一次,从而保证了线程安全性。
  3. 兼并了并发高效调用和延迟加载的优势

枚举实现

public enum  SingletonDemo05 {

    //定义一个枚举元素,它就代表了Singleton的一个实例
    INSTANCE;

    //单例可以有自己的操作
    public void singletonOperation() {
        //功能处理
    }
    
}

优点:

  1. 实现简单
  2. 枚举本省就是单例模式。由JVM从根本上提供保障,避免通过反射和序列化的漏洞

缺点:

无延迟加载

结语

有什么补充的欢迎大家留言,后面会把剩下的设计模式也写一下,如果觉得帮助到你了,就请点个赞,哈哈哈!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值