背景:
- 最近在看java设计模式,看了几篇关于“单例模式”的文章,决定自己实践一遍整理一下记录下来。
定义:
- 在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例。
特点:
- 单例类只能有一个实例;
- 单例类必须自己创建自己的唯一实例;
- 单例类必须给其它对象提供这一实例。
要点:
- 私有构造方法;
- 指向自己实例的私有静态引用;
- 以自己实例为返回值的静态的公有方法。
优点:
- 只存在一個實例,減少了內存的開支,特別是一個對象需要頻繁創建銷毀時,而且創建銷毀時性能又無法優化,單例模式的優勢就非常明顯;
- 由於單例模式只存在一個實例,所以減少了系統的性能開銷,當一個單例對象產生時如果需要比較多的資源時,則可以在應用啟動時
* 直接生產一個單例,然後永駐內存的方式來解決;
- 單例模式可以避免對資源的多重佔用,例如一個寫文件的操作,由於只存在一個實例在內存中,避免對同一個資源文件的同時寫操作;
- 單例模式可以在系統設置全局的訪問點,優化和共享資源訪問,例如,可以設計一個單列類,負責所有數據表的映射處理。
缺点:
- 單例模式一般沒有接口,擴展很困難,若要擴展,除了修改代碼基本上沒有第二種途徑可以實現;
- 隐式使用引起类结构不清晰;
- 單例對象如果持有Context,那麼很容易引起內存洩漏,此時需要傳給單例對象的Context最好是Application Context。
场景:
- 需要频繁实例化然后销毁的对象;
- 创建对象时耗时过多或者资源过多,但又经常用到的对象;
- 资源共享的情况下,避免由于资源操作时导致的性能损耗;
- 控制资源的情况下,方便资源之间的互相通信。
注意事项:
- 只能使用单例类提供的方法获得单例对象,不能使用反射,否则会实例化出新对象;
- 不要做断开单例类对象与类中静态引用的危险操作;
- 多线程使用单例共享资源时,注意线程安全问题。
代码示范(单例类SingletonDemo):
- 第1种:懒汉式(线程不安全)
- 代码:
private static SingletonDemo instance; private SingletonDemo() { } public static SingletonDemo getInstance() { if (null == instance) { instance = new SingletonDemo(); } return instance; }
- 优缺点:
- 优点:在用户第一次调用时才去初始化,节约资源,起到了懒加载的作用。
- 缺点:第一次调用时稍慢,因为要初始化,多线程不能正常工作。
- 第2种:懒汉(线程安全)
- 代码:
private static SingletonDemo instance; private SingletonDemo() { } public static synchronized SingletonDemo getInstance() { if (null == instance) { instance = new SingletonDemo(); } return instance; }
- 优缺点:
- 优点:在多线程中能很好的工作。
- 缺点:每次调用getInstance()时都需要进行同步,大部分时候都是用不到同步的,造成了资源的浪费,所以不建议使用。
- 第3种:饿汉式(天生的线程安全)
- 代码:
private static SingletonDemo instance = new SingletonDemo(); private SingletonDemo() { } public static SingletonDemo getInstance() { return instance; }
- 优缺点:
- 优点:餓漢式在創建類的同時就創建了靜態實例供對象系統調用,以後不再發生改變, 所以是線程安全的。
- 缺点:
- 类加载时就完成了初始化,没有达到懒加载的效果;
- 在类加载时就初始化了实例,如果想在創建實例前調用某個方法給它傳遞一個參數就變不到了。
- 第4种:DCL(双重检查模式)
- 代码:
private volatile static SingletonDemo instance; private SingletonDemo() { } public static SingletonDemo getInstance() { if (null == instance) { synchronized (SingletonDemo.class) { } if (null == instance) { instance = new SingletonDemo(); } } return instance; }
- 实现原理:
- 在getInstance()时对instance时进行了2次判空,第一次是为了不必要的同步,第二次是为了instance为null * 才创建实例。
- 优缺点:
- 优点:DCL的优点是资源利用率高,第一次执行getInstance()时实例对象才被实例化,效率高。
- 缺点:第一次加载时反应稍慢一点,而且有失效的可能。
- 第5种:静态内部类
- 代码:
private static class SingletonHolder { private static final SingletonDemo INSTANCE = new SingletonDemo(); } private SingletonDemo() { } public static final SingletonDemo getInstance() { return SingletonHolder.INSTANCE; }
- 实现原理:
- 第一次加载Singleton类时并不会初始化instance,只有第一次调用getInstance()方法时 * 虚拟机加载SingleHolder并初始化instance,这样不仅能确保线程安全,也能保证实例的唯一性,很合理的单例模式。
- 第6种:枚举类SingletonEnum
- 代码:
INSTANCE; public void whateverMethon() { }
- 优缺点:
- 优点:避免了线程安全,防止反序列化重新创建新的对象。
- 缺点:失去了类的一些特性,没有延时加载。
- 第7种:注册登记式
- 这个没看明白,不知道是我阅读的资料有问题还是自己理解力差。希望看到的小伙伴能给我讲一下,最好有可运行的代码示例。感激涕零!!!
总结:
- 以上是阅读了几篇文章之后自己尝试写了几个小例子,毕竟“纸上得来终觉浅,绝知此事要躬行”嘛。
参阅资料,非常感激:
- https://blog.csdn.net/itachi85/article/details/50510124
- https://blog.csdn.net/coder_pig/article/details/54411894
- https://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&mid=2247483786&idx=1&sn=072991244ec2f259c7049bc80317a728&scene=21#wechat_redirect