一. 前言
1. 定义
Singleton:确保某个类只有一个实例,并且自行实例化并向整个系统提供这个实例。
二. 实现方式
1. 饿汉式
1 2 3 4 5 6 7 8 9 10
| public class Singleton { private Singleton(){} private static final Singleton instance = new Singleton(); public static Singleton getInstance(){ return instance; } }
|
优点:写法简单,线程安全。
缺点:如果没有使用过的话会造成内存浪费,没有懒加载的效果。
2. 懒汉式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class Sinleton { private Singleton(){} private static Singleton instance = null; public static Singleton getInstacne(){ if (instance == null) { instance = new Singleton(); } return instance; } }
|
优点:实现了懒加载的效果。
缺点:线程不安全。
3. 懒汉式(线程安全)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class Sinleton { private Singleton(){} private static Singleton instance = null; public static synchronized Singleton getInstacne(){ if (instance == null) { instance = new Singleton(); } return instance; } }
|
优点:实现了懒加载的效果,线程安全。
缺点:使用 synchronized 会造成不必要的开销,而且大部分的时候我们是用不到同步的。
4. 双重锁定(DCL)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class Sinleton { private volatile static Singleton instance; private Singleton(){} private static Singleton instance = null; public static Singleton getInstacne(){ if (instance == null) { synchronized (instacne.class) { if (instacne == null) { instance = new Singleton(); } } } return instance; } }
|
优点:懒加载,线程安全,效率较高。
缺点:volatile影响一点性能,高并发下有一定的缺陷,某些情况下DCL会失效,虽然概率较小。
5. 静态内部类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Sinleton { private Singleton(){} public static Singleton getInstacne(){ return SingletonHolder.instance; } private static class SingletonHolder { private static final Singleton instance = new Singleton(); } }
|
优点:懒加载,线程安全,推荐使用。
6. 枚举单例
1 2 3 4
| public enum Sinleton { INSTNCE; }
|
优点:线程安全,写法简单,能防止反序列化重新创建新的对象。
缺点:可读性不高,枚举会比静态常量多那么一丁点的内存。
7. 使用集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class Sinleton { private static Map<String,Object> map = new HashMap<>; private static void registerInstance(String key, Object instance){ if (!map.contain(key)) { map.put(key,instance); } } public static Singleton getInstacne(String key){ return map.get(key); } }
|
优点:方便管理。
缺点:写法复杂。
三. 注意事项
1. 使用反射能够破坏单例模式,所以应该慎用反射
1 2 3 4 5 6 7
| Constructor con = Singleton.class.getDeclaredConstructor(); con.setAccessible(true);
Singleton singeton1 = (Singleton) con.newInstance(); Singleton singeton2 = (Singleton) con.newInstance(); System.out.println(singeton1==singeton2);
|
可以通过当第二次调用构造函数时抛出异常来防止反射破坏单例,以懒汉式为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class Singleton { private static boolean flag = true; private static Singleton single = null;
private Singleton() { if (flag) { flag = !flag; } else { throw new RuntimeException("单例模式被破坏!"); } }
public static Singleton getInstance() { if (single == null) { single = new Singleton(); } return single; } }
|
2. 反序列化时也会破坏单例模式
可以通过重写readResolve方法避免,以饿汉式为例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Singleton implements Serializable { private Singleton() {}
private static final Singleton single = new Singleton();
public static Singleton getInstance() { return single; }
private Object readResolve() throws ObjectStreamException { return single; } }
|
四. 优缺点以及应用场景
1. 优点
2. 缺点
3. 应用场景
- 频繁访问数据库或文件的对象。
- 工具类对象。
- 创建对象时耗时过多或耗费资源过多,但又经常用到的对象。
参考文章
Android设计模式-单例模式