单例模式:
角色:
- 单例类:提供单例的工厂,返回单例
- 使用者:获取并使用单例类
定义:结构只包含一个被称为单例的特殊类,通过单例模式可以保证一个类只有一个对象实体
特点:
- 单例类只能有一个实例
- 单例类必须自己创建自己的唯一实例
- 单例类必须给所有其他对象提供这一实例
要点:
- 私有的构造方法
- 指向自己实例的私有静态引用
- 以自己实例为返回值的静态的公有的方法
优点:
- 内存中只有一个对象,节约内存空间
- 避免频繁创建何销毁对象,可以提高性能
- 避免对共享资源的多重占用
- 可以全局访问
- 对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销
- 由于new操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻GC压力,缩短GC停顿时间
缺点:
- 扩展困难,没办法扩展,只能重写该类
- 隐式使用引起类结构不清晰
- 使用不当会导致程序内存泄漏的问题
注意事项:
- 只能通过该例提供的方法获取对象,不能使用反射,不然会实例化一个新对象
- 不要做断开单例对象与类中静态引用的危险操作。
因此对于系统的关键组件和被频繁使用的对象,使用单例模式便可以有效地改善系统的性能。
实例化时机分为:饿汉式单例(直接创建实例),懒汉式单例(访问时创建(访问的时候判断是否有实例,没有就创建))
并发的时候可能需要锁,所以有了双重锁和静态内部类的实现
四种实现:
1、饿汉式单例:
/**
* 痴汉模式单例
* 不管三七二十一,先实例化出来再说(类加载完成的时候就完成单例的实例)
* 优点:采取空间换时间的思路,不管用不用,先实例化出来.
* 缺点:不用的时候提前实例化出来了,浪费空间
*/
public class CrazySingleton {
/**
* 类加载的时候就实例化单例
*/
private static CrazySingleton singleton = new CrazySingleton();
/**
* 向外暴露获取单例的方法
* @return
*/
public static CrazySingleton getInstance(){
return singleton;
}
}
2、懒汉式单例:
/**
* 懒加载的单例
* 优点:延迟加载,时间换空间策略,等到实际第一次用的时候,再去完成初始化
* 缺点:加了锁,在并发高的时候,锁的竞争激烈,对性能有一定影响
*
*/
public class LazySingleton {
/**
* 先不实例化,等需要用到的时候进行实例化
* 延迟加载,
* 因为一个静态变量的实例化可能会比较久,而且类加载以后不一定马上使用,所以没有必要在类加载的时候就马上实例化出来
*/
private static LazySingleton singleton;
/**
* 通过synchronized来进行同步,防止两个进程同时进入方法进行单例的实例化
* @return
*/
public synchronized static LazySingleton getInstance(){
if (singleton != null){
singleton = new LazySingleton();
}
return singleton;
}
}
3、双重检查单例模式
/**
* 双重检查的单例模式
* 饿汉+懒汉
* 优点:延迟加载,不用时节约空间,保证线程安全
* 缺点:volatile使得每次都会取内存里面取值,会导致轻微的性能损耗
*/
public class DCLSingleton {
//加volatile关键字,防止指令重排后,未初始话完成对象就返回堆引用
private volatile static DCLSingleton singleton;
/**
* 私有化构造方法
*/
private DCLSingleton(){}
/**
* 双重检查
* 以防获取锁以后别的线程已经完成了单例的实例化,确保实例化过程只执行一次
* @return
*/
public static DCLSingleton getInstance(){
if (singleton == null){
synchronized(DCLSingleton.class){
if (singleton == null){
singleton = new DCLSingleton();
}
}
}
return singleton;
}
}
4、静态内部类单例模式
/**
* 内部类单例
* 优点:延迟加载,不需要同步关键字实现,对多线程良好
* 缺点:不能传参
*/
public class StaticSingleton {
/**
* 私有化构造方法
* 保证其他类无法主动实例化出该单例
*/
private StaticSingleton(){}
/**
* 定义内部类,
*/
private static class SingletonHolder{
private static StaticSingleton instance = new StaticSingleton();
}
/**
* 从内部类里面获取实例,实现延迟加载以及同步
* 当该类被类加载器加载的时候,内部类会被加载,但不会实例化出里面的静态变量
* 而且单例是在内部类加载的时候完成的,因此对多线程友好,因为一个类不会被加载多次
* @return
*/
public static StaticSingleton getInstance(){
return SingletonHolder.instance;
}
}
四种方式建议使用后两种,优点明显,代码里面都有说明,这里就不赘述了。