单例模式

简介: 本文主要介绍内容:1. 单例及单例模式概念2. 实现单例模式注意点3. 单例模式的实现方式

单例及单例模式概念

一个类只允许创建一个实例。这种设计模式就是单例模式

         1617762041548.png


实现单例模式需要考虑什么?

  • 构造函数私有,避免外部通过new来创建实例
  • 考虑是否需要延时加载
  • 考虑线程安全


单例模式的几种实现方式

1. 饿汉式:在类加载的时候,就已经初始化实例了。

publicclassHungry {
// 一启动就被加载,还未使用就占用内存,造成资源浪费privatestaticfinalHungryINSTANCE=newHungry();
// 构造方法私有,外部无法进行实例化privateHungry() {}
// 获取实例对象publicstaticHungrygetInstance() {
returnINSTANCE;
  }
}


2. 懒汉式:第一次使用时才进行实例化

publicclassLasy {
// 启动时不加载,需要时再进行实例化privatestaticLasyINSTANCE;
// 构造方法私有,外部无法进行实例化privateLasy() {}
publicstaticLasygetInstance() {
if (null==INSTANCE) {
INSTANCE=newLasy();
        }
returnINSTANCE;
    }
}

但是懒汉式并不能保证单例,在多线程并发情况下,会多次调用构造方法进行实例化,示例如下:

publicclassLasy {
// 启动时不加载,需要时再进行实例化privatestaticLasyINSTANCE;
// 构造方法私有,外部无法进行实例化privateLasy() {
System.out.println(Thread.currentThread().getName());
    }
publicstaticLasygetInstance() {
if (null==INSTANCE) {
INSTANCE=newLasy();
        }
returnINSTANCE;
    }
publicstaticvoidmain(String[] args) {
for (inti=0; i<10; i++) {
newThread(Lasy::getInstance).start();
        }
    }
}

输出结果:

2.png


2.1 使用同步锁synchronized

//如果使用synchronized关键字,这种方式在并发下并发量等同于1,如果频繁使用该对象,会大大降低性能publicstaticsynchronizedLasygetInstance() {
if (null==INSTANCE) {
INSTANCE=newLasy();
    }
returnINSTANCE;
}


2.2 双重检测(DCL懒汉式单例)

publicstaticLasygetInstance() {
if (null==INSTANCE) {
synchronized (Lasy.class) {
if (null==INSTANCE) {
INSTANCE=newLasy();
            }
        }
    }
returnINSTANCE;
}
// INSTANCE = new Lasy();/** 以上操作并不是原子性操作,包括以下三个步骤:* 1. 分配内存空间* 2. 执行构造方法进行初始化* 3. 将对象指向这个空间*//** 在多线程情况下可能出现指令重排,每个线程执行INSTANCE = new Lasy()这个操作的顺序可能不一致,那么就有可能出现线程A先执行了第一步和第三步,此时还未进行初始化,线程B执行判断语句null == INSTANCE时为false,将直接返回线程A未进行初始化的INSTANCE对象,所以要避免指令重排,使用volatile关键字。*/privatestaticvolatileLasyINSTANCE;
publicstaticLasygetInstance() {
if (null==INSTANCE) {
// 对象未被实例化,等待类对象锁synchronized (Lasy.class) {
// 进入对象锁,其他线程等待,再次判断是否被实例化if (null==INSTANCE) {
INSTANCE=newLasy();
            }
        }
    }
returnINSTANCE;
}

饿汉式和懒汉式比较:
①由于饿汉式是在类加载的时候进行实例化,线程安全;懒汉式是在第一次使用的时候进行实例化,此时可能多线程会同时进行实例化,所以线程不安全;
②饿汉式不支持延时加载,如果占用资源较多或者耗时较长,可能会影响启动时间;懒汉式支持延时加载,但是需要使用同步锁,如果这个类被频繁使用,可能导致性能问题。(相比较更加倾向于饿汉式单例,在启动时进行加载,如果遇到性能或者资源问题会提前暴露,相较于正在运行中出现问题更加可控)


3. 静态内部类

publicclassTest {
privateTest() {}
privatestaticclassInnerTest{
// final修饰变量是不可变的,初始化对象之后便不会再指向另一个对象privatestaticfinalTestINSTANCE=newTest();
    }
publicstaticTestgetInstance() {
returnInnerTest.INSTANCE;
    }
}

但是在反射机制下,所有的私有方法都能被破解,如下实例表示通过静态内部类获得的实例对象和通过反射获得的holder实例对象并不是同一个,仍然破坏了单例

publicstaticvoidmain(String[] args) throwsException {
// 获取private的构造方法Constructor<Test>constructor=Test.class.getDeclaredConstructor(null);
// 无视私有构造器constructor.setAccessible(true);
Testtest=constructor.newInstance();
System.out.println(Test.getInstance().equals(test));
}
输出结果:false

通过反射的源码可以看到,无法反射式创建枚举对象,所以我们可以考虑使用枚举来实现单例

1.png

4. 枚举

publicenumSingleEnum {
INSTANCE;
}


以下示例可见确实无法反射式创建枚举对象,当使用反射创建枚举对象时,会抛出异常信息Cannot reflectively create enum objects

publicstaticvoidmain(String[] args) throwsException {
// 获取private的构造方法(实际上枚举类的构造方法带有两个参数,并不是我们从源码看到的无参构造,需要使用源码工具来查看)Constructor<SingleEnum>constructor=SingleEnum.class.getDeclaredConstructor(String.class, int.class);
// 无视私有构造器constructor.setAccessible(true);
SingleEnumsingle=constructor.newInstance();
System.out.println(SingleEnum.INSTANCE.equals(single));
}

输出:

3.png

相关文章
|
2月前
|
设计模式 安全 测试技术
【C++】—— 单例模式详解
【C++】—— 单例模式详解
|
2月前
|
设计模式 安全
详细讲解什么是单例模式
详细讲解什么是单例模式
|
24天前
|
设计模式 安全 Java
单例模式
​ 如有错误或有补充,以及任何的改进意见,请在评论区留下您的高见,同时文中给出大部分的示例 如果觉得本文写的不错,不妨点个赞,收藏一下,助力博主产生质量更高的作品 概念 单例模式(Singleton Pattern)是软件设计模式的一种,用于确保一个类只有一个实例,并提供一个全局访问点。这种模式通常用于需要频繁创建和销毁同一对象的场景,以减少系统资源的消耗和提高性能。 优缺点 优点: 实例控制:单例模式确保类只有一个实例,可以防止其他对象实例化自己的副本,从而确保所有对象都访问唯一实例。 节约资源:由于系统中只存在一个对象,可以节约系统资源,特别是在需要频繁创建和销毁对象的场景中,可
27 0
|
9月前
|
设计模式 安全 编译器
2023-6-12-第三式单例模式
2023-6-12-第三式单例模式
51 0
|
5月前
|
安全 C++
C++单例模式
C++单例模式
32 1
|
5月前
|
设计模式 安全
【单例模式】—— 每天一点小知识
【单例模式】—— 每天一点小知识
|
9月前
|
安全 Java
原来要这么实现单例模式
原来要这么实现单例模式
38 0
|
设计模式 安全 Java
回顾一下单例模式
回顾一下单例模式
|
设计模式 安全 前端开发
关于单例模式,你应该了解这些
关于单例模式,你应该了解这些
关于单例模式,你应该了解这些
|
设计模式 缓存
我学会了,单例模式
单例模式属于创建型模式,这个类型的设计模式是将 对象的创建和使用解耦了,花式的去创建对象。
106 0
我学会了,单例模式