开发者社区> miaoikxm> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

单例模式的理解

简介: 谈谈你对单例模式的理解。也算是老生常谈的问题了~~~
+关注继续查看
福利推荐:阿里云、腾讯云、华为云等大品牌云产品全线2折优惠活动来袭,4核8G云服务器899元/3年,新老用户共享优惠,点击这里立即抢购>>>

一、作用

单个实例

保证我们的实例对象在整个应用程序中只有一个实例

二、创建方式

五种实现方式

2.1 饿汉式

在类加载的时候立即初始化,并且创建单例对象

它绝对线程安全,在线程出现以前就实例化了,不可能存在访问安全问题

优点:不加任何锁,执行效率比较高,用户体验比懒汉单例模式好

缺点:浪费内存,不管用不用都占着内存

public class HungrySingleton {

    // 直接实例化方式
//    private static final HungrySingleton hungrySingleton = new HungrySingleton();
    private static final HungrySingleton hungrySingleton;
    // 静态块单例模式
    static {
        hungrySingleton = new HungrySingleton();
    }
    private HungrySingleton(){}

    private static HungrySingleton getInstance(){
        return hungrySingleton;
    }
}

2.2 懒汉式

在外部调用的时候才进行实例化,相比较饿汉式避免了资源浪费

在单线程情况下,比较友好。

多线程情况下,会存在线程安全问题

public class LazySimpleSingleton {

    private LazySimpleSingleton() {
    }

    private static LazySimpleSingleton lazySimpleSingleton = null;

    public static LazySimpleSingleton getInstance() {
        if (null == lazySimpleSingleton) {
            lazySimpleSingleton = new LazySimpleSingleton();
        }
        return lazySimpleSingleton;
    }
}

2.3 双重检测锁(DCL)

基于懒汉式的线程安全问题,有了双重校验锁

public class LazyDoubleCheckSingleton {
    private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton = null;

    /**
     * 私有化构造方法,防止直接通过new实例化
     */
    private LazyDoubleCheckSingleton() {
    }

    public static LazyDoubleCheckSingleton getInstance() {
        if (lazyDoubleCheckSingleton == null) {
            synchronized (LazyDoubleCheckSingleton.class) {
                if (lazyDoubleCheckSingleton == null) {
                    // 1.分配内存空间 2.执行构造方法,实例化对象 3.把这个对象赋给这个空间
                    // 不加volatile关键字,会造成指令重排,1,3,2
                    lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
                }
            }
        }
        return lazyDoubleCheckSingleton;
    }
}

2.4 静态内部类

public class StaticSingleton {

    public static class InnerStaticSingleton {
        /**
         * 声明外部类型的静态常量
         */
        public static final StaticSingleton instance = new StaticSingleton();
    }
    
    private StaticSingleton() {
    }

    public StaticSingleton getInstance() {
        return InnerStaticSingleton.instance;
    }
}

2.5 枚举类型

public enum  EnumSingleton {
    INSTANCE;

    public void handleMethod(){
        // 业务处理
    }
}

综上的五种写法,大多都是在考虑着线程安全问题

2.6 反射爆破问题

私有的构造器,可以通过反射去破坏。

在私有构造器中进行判断,进而抛出异常。

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    LazySimpleSingleton instance = LazySimpleSingleton.getInstance();

    Class<LazySimpleSingleton> clazz = LazySimpleSingleton.class;
    Constructor<LazySimpleSingleton> constructor = clazz.getDeclaredConstructor();
    constructor.setAccessible(true);
    LazySimpleSingleton instance1 = constructor.newInstance();

    System.out.println(instance);
    System.out.println(instance1);
}

// 结果
com.example.validated.design.singleton.LazySimpleSingleton@6d5380c2
com.example.validated.design.singleton.LazySimpleSingleton@45ff54e6

2.7 序列化与反序列化破坏单例

LazySimpleSingleton要实现Serializable序列化接口

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, ClassNotFoundException {
    LazySimpleSingleton instance = LazySimpleSingleton.getInstance();

    Class<LazySimpleSingleton> clazz = LazySimpleSingleton.class;
    Constructor<LazySimpleSingleton> constructor = clazz.getDeclaredConstructor();
    constructor.setAccessible(true);
    LazySimpleSingleton instance1 = constructor.newInstance();

    System.out.println(instance);
    System.out.println(instance1);

    ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("d:/tools/a.txt"));
    outputStream.writeObject(instance);
    outputStream.flush();
    outputStream.close();

    ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("d:/tools/a.txt"));
    LazySimpleSingleton instance2 = (LazySimpleSingleton) inputStream.readObject();
    inputStream.close();
    System.out.println(instance);
    System.out.println(instance2);
}

// 结果
com.example.validated.design.singleton.LazySimpleSingleton@6d5380c2
com.example.validated.design.singleton.LazySimpleSingleton@45ff54e6
com.example.validated.design.singleton.LazySimpleSingleton@6d5380c2
com.example.validated.design.singleton.LazySimpleSingleton@5e265ba4

我们需要重写readResolve()方法

private Object readResolve() {
    return lazySimpleSingleton;
}

结果:

com.example.validated.design.singleton.LazySimpleSingleton@6d5380c2
com.example.validated.design.singleton.LazySimpleSingleton@45ff54e6
com.example.validated.design.singleton.LazySimpleSingleton@6d5380c2
com.example.validated.design.singleton.LazySimpleSingleton@6d5380c2

说明:readResolve()方法是基于回调的,反序列化时,如果定义了readResolve()则直接返回此方法指定的对象,而不需要再创建新的对象。

三、应用

在框架中看到的单例模式

  1. Spring中的Bean对象,默认是单例模式
  2. 相关的工厂对象都是单例,如:Mybatis中的SqlSessionFactory,Spring中BeanFactory
  3. 保存相关配置信息的都是单例,如:Mybatis中的Configuration对象,SpringBoot中的各个xxxAutoConfiguration对象
  4. 应用程序的日志应用,一般都会通过单例来实现
  5. 数据库的连接池的设计也是单例模式

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
透彻理解单例模式
主要内容有: 该模式的介绍,包括: 引子、意图(大白话解释) 类图、时序图(理论规范) 该模式的代码示例:熟悉该模式的代码长什么样子 该模式的优缺点:模式不是万金油,不可以滥用模式 该模式的实际使用案例:了解它在哪些重要的源码中被使用
56 0
单例模式
什么是单例模式?在一个项目中,单例模式确保某一个类只能有一个实例,这个类称为单例类。怎样确保某个类只能有一个实例,通常调用方法有两种方式: 创建类的一个对象,即new出一个对象,用该对象去调用类中方法; 使用类名直接调用类中方法,格式“类名.方法名()”想要类只有一个实例,必不能随意通过new出对象,禁止new对象就只能私有化构造方法,采用“类名.方法名()”这种方式获取方法。
1269 0
单例模式
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Hanniel/article/details/85212374 ...
825 0
单例模式
单例模式的定义如下: 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。 单例类自身保存它的唯一实例,这个类保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。 单例模式的一些特点: 构造方法私有化,防止外部通过访问构造方法创建对象; 提供一个全局方法使其单例对象被外部访问; 考虑多线程并发情况的单例唯一性。
1102 0
单例模式
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_40254498/article/details/79133275 懒...
540 0
单例模式
所有单例模式都有一个共性,那就是这个类没有自己的状态。也就是说无论这个类有多少个实例,都是一样的;然后除此者外更重要的是,这个类如果有两个或两个以上的实例的话程序会产生错误。 基于上述原因,非线程安全的实现方式,在此不再讨论。
565 0
单例模式
1.1.?单例模式 写设计模式的时候,我在思考为什么要写设计模式,正如鲁迅先生说的:世界上本来是没有路的,走得多了就有路了。设计模式也是先生所讲的一样,别人已经发明了一个轮子,后人只需要使用即可。
782 0
+关注
文章
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载


http://www.vxiaotou.com