单例模式(创建型设计模式)
分为 :饿汉式单例模式、懒汉式单例模式、线程安全的懒汉式单例模式;
标准回答 :
- 含义:一个类仅有一个实例化对象,其最大特点是构造函数私有化;
- 特点:构造函数私有化,从而避免外界直接使用构造函数直接实例化该类的对象。
- 单例模式主要分为饿汉式,懒汉式,线程安全的懒汉式。
- 饿汉式在类加载是就创建了实例对象,但是这样有可能造成资源的浪费;
- 懒汉式,在类第一次被调用时创建实例,这样可能会造成线程安全问题;
- 线程安全的懒汉式,则是在懒汉式的基础上加锁
一、单例模式要做的
- 1、单例类只能有一个实例。
- 2、单例类必须自己创建自己的唯一实例。(构造函数私有化)
- 3、单例类必须给所有其他对象提供这一实例。(通过公有的getInstance方法,提供一个全局访问点)
二、单例模式主要解决的问题:
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
三、单例模式使用场景:
- 1、要求生产唯一序列号。
- 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
- 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
1.饿汉式单例模式: 在类加载时就会初始化静态变量实例,这时候类的私有构造函数就会被调用,创建唯一的实例。
# 是否多线程安全:是;它基于 classloader 机制避免了多线程的同步问题
# 其他描述:
这种方式比较常用,但容易产生垃圾对象。
- 优点:没有加锁,执行效率会提高。
- 缺点:类加载时就初始化,浪费内存。
public class Singleton{
//在类加载时就会初始化静态变量实例
private static Singleton instance = new Singleton();
// 构造方法私有,确保外界不能直接实例化
private Singleton(){}
//通过公有的静态方法获取实例对象
public static Singleton getInstance(){
return instance;
}
}
2.懒汉式单例模式: 类在加载时不会初始化静态变量instance,而是在第一次被调用时将自己初始化。
# 是否多线程安全:否;因为没有加锁 synchronized
# 其他描述:
这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程,不要求线程安全,在多线程不能正常工作。
public class Singleton {
//先置空,在调用的时候实例化对象
private static Singleton instance = null;
// 私有构造方法,确保外界不能直接实例化。
private Singleton() {}
// 通过公有的静态方法获取对象实例
public static Singleton getInstace() {
if (instance == null) {
instance = new Singleton(); }
return instance;
}
}
问题:
如果线程A和B同时调用此方法,会出现执行if (instance == null)
语句时都为真的情况,那么线程A和B都会创建一个对象,那内存中就会出现两个对象,这违反了单例模式的定义。
如何解决:线程安全的懒汉式
为解决这一问题,使用synchronized
关键字对静态方法 getInstance()
进行同步,防止多线程同时进入造成 instance 被多次实例化。
3.线程安全的的懒汉式单例模式:
# 是否多线程安全:是;因有加锁 synchronized
# 其他描述:
这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。
public class Singleton {
private static Singleton instance = null;
// 私有构造方法,确保外界不能直接实例化。
private Singleton() {}
// 通过公有的静态方法获取对象实例
synchronized public static Singleton getInstace() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
4.饿汉式和懒汉式的对比:
饿汉式单例类在资源利用效率上不如懒汉式单例类,但从速度和反应时间来看,饿汉式单例类要优于懒汉式单例类。
资源利用效率:饿汉式<懒汉式;
速度和反应上:饿汉式>懒汉式;
5.单例模式优缺点:
单例模式的优点:在一个对象需要频繁的销毁、创建;而销毁、创建性能又无法优化时,单例模式的优势尤其明显 ;
- 在内存里只有一个实例,减少了内存的开销;尤其是频繁的创建和销毁实例
- 在一个对象的产生需要比较多资源时,如读取配置、产生其他依赖对象时,则可以通过在启用时直接产生一个单例对象,然后用永久驻留内存的方式来解决;
- 单例模式可以避免对资源的多重占用;因为只有一个实例,避免了对一个共享资源的并发操作;
- 单例模式可以在系统设置全局的访问点,优化和共享资源访问;
单例模式的缺点:
- 单例模式没有接口,不能继承;无法创建子类,扩展困难;若要扩展,除了修改代码基本上没有第二种途径可以实现;
- 单例模式对测试不利;在并行开发环境中,如果采用单例模式的类没有完成,是不能进行测试的;
- 单例模式与单一职责原则有冲突;一个类应该只实现一个逻辑,而不关心它是否是单例的,是不是要用单例模式取决于环境