单例模式

单例模式的定义

单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象;一个最好的办法就是:让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且提供一个访问该实例的方法。

单例模式的实现

饿汉式

  • 构造函数私有化
  • 迫不及待创建静态对象
  • 公有方法返回对象
package ASingleTon;

/**
 * @Author Zhou  jian
 * @Date 2020 ${month}  2020/6/26 0026  20:58
 * 饿汉式单例
 * 可能会浪费空间
 */
public class Hungry {


    //可能会浪费大量的内存空间
    private int[] a = new int[1024];
    private int[] a1 = new int[1024];


    //1、构造器私有化
    private Hungry(){}

    private final static Hungry HUNGRY = new Hungry();

    //
    public static Hungry getInstacne(){
        return HUNGRY ;
    }




}

可能会造成内存的浪费,上来加载大量对象到内存
可能被反射破坏

懒汉式 --DCL+volitale

  • DCL:双重检测
  • voliatle静态关键字
package ASingleTon;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * @Author Zhou  jian
 * @Date 2020 ${month}  2020/6/26 0026  21:01
 *
 * DCL懒汉式单例:可以使用反射破坏
 */
public class LazyMan {


    //构造器私有化
    private LazyMan(){}

    //加入volitale防止指令重拍
    private static  volatile LazyMan lazyMan;

    //DCL:双重检测模式下啊的懒汉单例模式
    public static LazyMan getInstance() {
        if(lazyMan==null)
            synchronized (LazyMan.class){
                if(lazyMan==null){
                    //这个不是原子性操作
                    /**
                     * 1、分配内存空间
                     * 2、执行构造方法,初始化对象
                     * 3、把这个对象指向这个空间
                     */
                    lazyMan = new LazyMan();
                }
            }

        return lazyMan;//此时lazyman 还没有完成
    }


    //反射破解
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

        LazyMan instance = LazyMan.getInstance();
        Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);
        //无视私有构造器
        constructor.setAccessible(true);

        LazyMan lazyMan1 = constructor.newInstance();


        System.out.println(lazyMan1);//ASingleTon.LazyMan@4554617c
        System.out.println(instance);//ASingleTon.LazyMan@74a14482

    }


}


由于饿汉式,即静态初始化的方式,它是类一加载就实例化的对象,所以要提前占用系统资源;而懒汉式,又会面临着多线程访问的安全性问题,需要做双重锁定这样的处理才可以保证安全

静态内部类


package ASingleTon;

/**
 * @Author Zhou  jian
 * @Date 2020 ${month}  2020/6/26 0026  21:11
 * 静态内部类实现
 *
 *   这几种方法都是不安全的
 */
public class Holder {

    //构造器私有化
    private Holder(){}

    public static  Holder getInstance(){
        return InnerClass.HOLDER;
    }

    public static class InnerClass{

        private static final Holder HOLDER = new Holder();
    }


}

枚举实现(反射不能破坏)


package ASingleTon;

/**
 * @Author Zhou  jian
 * @Date 2020 ${month}  2020/6/26 0026  21:18
 * 枚举本身也是一个Class类
 *  枚举是线程安全的,并且反射不能破坏枚举
 */
public enum  EnumSingle {


    INSTANCE;

    public EnumSingle getInstance(){
        return INSTANCE;
    }




}

单例模式的使用场景

下面几个场景中使用单例模式:

  • 有频繁实例化然后销毁的情况,也就是频繁的 new 对象,可以考虑单例模式
  • 创建对象时耗时过多或者耗资源过多,但又经常用到的对象;
  • 频繁访问 IO 资源的对象,例如数据库连接池或访问本地文件;

配置文件访问类

项目中经常需要一些环境相关的配置文件,比如短信通知相关的、邮件相关的。比如 properties 文件,这里就以读取一个properties文件配置为例,如果你使用的 Spring ,可以用 @PropertySource 注解实现,默认就是单例模式。如果不用单例的话,每次都要 new 对象,每次都要重新读一遍配置文件,很影响性能,如果用单例模式,则只需要读取一遍就好了。

数据库连接池的实现

数据库连接池的实现,也包括线程池。为什么要做池化,是因为新建连接很耗时,如果每次新任务来了,都新建连接,那对性能的影响实在太大。所以一般的做法是在一个应用内维护一个连接池,这样当任务进来时,如果有空闲连接,可以直接拿来用,省去了初始化的开销。所以用单例模式,正好可以实现一个应用内只有一个线程池的存在,所有需要连接的任务,都要从这个连接池来获取连接。如果不使用单例,那么应用内就会出现多个连接池,那也就没什么意义了。如果你使用 Spring 的话,并集成了例如 druid 或者 c3p0 ,这些成熟开源的数据库连接池,一般也都是默认以单例模式实现的。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值