介绍
单例是应用最广的模式之一,也可能是很多初级工程师唯一会用的设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候系统只需要拥有一个全局对象,这样有利于我们协调系统整体的行为。如在一个应用中,应该只有一个ImageLoader实例,这个ImageLoader又含有线程池、缓存系统、网络请求等,很消耗资源,因此没有理由让它构建多个实例。这种不能自由构建对象的情况,就是单例模式的使用场景。
定义
确保一个类只有一个实例,而且自行实例化并向整个系统提供整个实例。
使用场景
确保某个类有且只有一个实例的场景,避免产生多个对象消耗过多资源,或者某种类型的对象只应该有且只有一个。例如,创建一个对象需要消耗的资源过多,如访问IO和数据库等资源。这个时候就要考虑只用单例模式。
角色介绍
1、Client----高层客户端
2、Singleton---单例类。
实现单例模式主要有一下几个关键点
1、构造函数不对外开发,一般为private
2、通过一个静态方法或者枚举返回单例类实例。
3、确保单例类对象有且只有一个,尤其是在多线程环境下。
4、确保单例类对象在反序列化时不会重新构建对象
通过将单例类的构造函数私有化,使得客户端代码不能通过new的形式创建单例对象。单例类会暴露一个公有静态方法,客户端需要调用这个静态方法获取到单例类的唯一对象,在获取这个单例对象的过程中要保证线程安全,即在多线程环境下构造单例类的对象也是有且仅有一个,这也是单例实现中比较困难的地方。
示例
/**
*
* 饿汉式
*/
public class Singleton2 {
private Singleton2() {//私有化构造方法
}
private static Singleton2 singleton2 = new Singleton2();//私有化创建对象
public static Singleton2 getInstance() {
return singleton2;
}
}
/**
* 懒汉模式
*/
public class Singleton1 {
private Singleton1(){
}
private volatile static Singleton1 singleton1;//第一层锁:保证变量可见
public static Singleton1 getInstance(){
if(singleton1==null){//第一层判空:不要每次都加锁,提升性能
synchronized (Singleton1.class){//第二层锁:保证线程同步
if (singleton1==null){//第二次判空:避免多线程同时getInstance产生多个对象
singleton1=new Singleton1();
}
}
}
return singleton1;
}
}
/**
* 当第一次加载Singleton类时并不会初始化singleton,只有在第一次调用getInstance()
* 才会导致singleton被初始化。因此,第一次调用getInstance()会导致虚拟机加载
* SingletonHolder类,这种方式不仅能确保线程安全,也能保证单例对象的唯一性,同时也延迟了单例
* 的实例化,所以这才是最推荐的单例模式的实现方式。
*/
public class Singleton {
private Singleton(){
}
public static Singleton getInstance(){
return SingletonHolder.singleton;
}
/**
* 静态内部类
*/
private static class SingletonHolder{
private static final Singleton singleton=new Singleton();
}
}