在生活中我们经常会遇到这种情况:只能有一个对象存在。例如一个团队中只能有一个最高领导者、在windows系统中只能有一个资源管理器弹窗,这就需要使用单例模式来实现。
单例模式使我们在开发过程中最常用、最简单的设计模式,属于创建型模式,这种模式确保在整个程序中只能存在一个实例。
使用单例模式,增加了对象的复用率,减少了存储的压力,是一种最佳创建对象的方法。
单例模式的关键代码主要有三个点:
1、单例类只能有一个实例,可以通过同步或类加载原理实现;
2、单例类必须自己创建唯一的实例。所以需要将构造方法私有化;
3、单例类必须提供一个获取单例实例的方法。
本篇文章主要介绍6中常用单例。
1、懒汉式(线程不安全)
/** * 懒汉式 线程不安全 * @author 樱桃肉丸子 */ public class SinglePattern1 { /** * 将单例静态化、私有化 */ private static SinglePattern1 instance; /** * 私有化构造方法 */ private SinglePattern1(){} /** * 提供获取唯一实例方法 * @return */ public static SinglePattern1 getInstance(){ if(instance != null){ return instance; } return new SinglePattern1(); } }
所谓懒汉式就是在需要用到实例的时候才会实例化单例对象。这种方式是最简单的单例模式,但当多个线程进行访问的时候,并没有做到真正的实例,所以在开发过程中不推荐该方法。
2、懒汉式(线程安全)
/** * 懒汉式 线程安全 * @author 樱桃肉丸子 */ public class SinglePattern2 { /** * 单例私有化、静态化 */ private static SinglePattern2 instance; /** * 私有化构造方法 */ private SinglePattern2(){} /** * 提供获取唯一实例方法 同时加锁 * @return */ public static synchronized SinglePattern2 getInstance(){ if(instance == null){ return new SinglePattern2(); } return instance; } }
这种方法是懒汉式的另一种实现,通过同步关键字来修饰保证线程安全。但加锁会影响到性能,所以在并发量较大的情况下不推荐使用该方法。
3、饿汉式
/** * 饿汉式 * @author 樱桃肉丸子 */ public class SinglePattern3 { /** * 私有化、静态化实例 */ private static SinglePattern3 instance = new SinglePattern3(); /** * 私有化构造方法 */ private SinglePattern3 (){} /** * 提供获取唯一实例方法 * @return */ public static SinglePattern3 getInstance(){ return instance; } }
所谓饿汉式就是在类加载的时候就创建了实例,同时也是线程安全的。与上一种方式不同的是,这种方法通过类加载原理实现整个程序中只有这一个实例,展开说就是静态变量只会在类加载的时候进行声明和实例化。但这种方式也存在缺点,即在类加载的时候就会实例化但该对象并不会用到,所以占用了部分内存。在开发过程中,最经常用到的就是这种方法。
4、双检索式
/** * 双检索式 * @author 樱桃肉丸子 */ public class SinglePattern4 { /** * 单例静态化,同时用volatile修饰保证所有线程读取的是一个实例 */ private volatile static SinglePattern4 singleton; /** * 私有化构造方法 */ private SinglePattern4 (){} /** * 提供获取唯一实例方法 * @return */ public static SinglePattern4 getInstance() { if (singleton == null) { synchronized (SinglePattern4.class) { if (singleton == null) { singleton = new SinglePattern4(); } } } return singleton; } }
双检索式是第二种和第三种的结合体,在线程安全的情况下依然保持着高性能。
5、静态内部类式
/** * 静态内部类式 * @author 樱桃肉丸子 */ public class SinglePattern5 { /** * 定义私有静态内部类 */ private static class SingletonHolder { private static final SinglePattern5 INSTANCE = new SinglePattern5(); } /** * 私有化构造方法 */ private SinglePattern5 (){} /** * 提供获取唯一实例方法 * @return */ public static final SinglePattern5 getInstance() { return SingletonHolder.INSTANCE; } }
这种方式使用静态内部类来实现,也是使用类加载的原理实现的线程安全,但与第三种不同的是,这种方式在类加载的时候并不会立刻创建单例,属于懒加载。所以这种方式相对于第三种方式来讲更合理。
6、枚举
/** * 枚举 * @author 樱桃肉丸子 */ public enum SinglePattern6 { INSTANCE; public void whateverMethod() { } }
这是实现单例模式的最佳方法,支持序列化,但这种模式还没有完全被普及,在工作过程中其实很少用到。
总结:
一般情况下不推荐使用懒汉式单例,推荐使用饿汉式单例。如果有明确的懒加载需求,可以使用静态内部类方式实现,若涉及到序列化等问题可以使用枚举方式,其他特殊需求可以考虑双检索方式。