摘要:总结单例是运用频率很高的模式,因为客户端没有高并发的情况,选择哪种方式并不会有太大的影响,出于效率考虑,推荐使用和静态内部类实现单例模式。
单例模式介绍
单例模式是应用最广的模式之一,也可能是很多人唯一会使用的设计模式。在应用单例模式时,单例对象的类必须保证只用一个实例存在。许多时候整个系统只需要一个全局对象,这样有利于我么能协调整个系统整体的行为。单例模式的使用场景
确保某个类有且只有一个对象的场景,避免创建多个对象消耗过多的资源,或者某个对象只应该有且只有一个。例如,创建一个对象需要消耗的资源过多,如要访问IO和数据库的资源,需要频繁进行创建和销毁的对象,这时候就需要考虑使用单例模式。单例的实现方式 饿汉式
public class Singleton { private static Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return instance; } }懒汉式
public class Singleton { private static Singleton instance; private Singleton() { } public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
getInstance()方法中添加了synchronized关键字,也就是getInstance是一个同步方法,在多线程情况下保证单例的对象唯一性的手段。但是会发现个问题即使instance已经被初始化,每次调用getInstance方法都会进行同步,这样会消耗不必要的资源,这也是懒汉式的最大的文问题。最后总结下:Double Check Lock(DCL)实现单例
优点:单例只在使用时才被初始化,一定程度上节约了资源。
缺点:第一次加载时需要进行实例化,反应稍慢,最大问题是每次调用getInstance都会进行同步,造成不必要的同步开销,这种模式一般不建议使用。
public class Singleton { private static Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
getInstance方法中对instance进行了两次判空,第一层判断主要是避免不必要的同步,第二层判断是为了在null放的情况下创建实例。由于Java编译器允许处理器乱序执行,在JDK1.5之前JMM(Java内存模型)会偶尔失败,会发生DCL失效问题。JDK1.5之后,SUN调整了JVM,又优化了volatile关键字,只要将instance定义改成 private volatile static Singleton instance = null就可以保证每次都是从主内存读取,就可以使用DCL的写法来完成单例。静态内部类单例模式
优点资源利用率高,既能在需要时才初始化单例,又能保证线程安全,且单例对象初始化后调用getInstance不进行同步,效率高。
缺点第一次加载反应稍慢,也由于JMM的原因偶尔会失败。在高并发环境下也有一定缺陷,虽然发生的概率较小。
DCL模式是使用最多的单例模式实现方式,除非代码在并发场景比较复杂或者JDK1.6以下版本使用,否则,这种方式基本都能满足需求。
public class Singleton { private Singleton() { } public static Singleton getInstance() { return SingletonHolder.instance; } private static class SingletonHolder { private static Singleton instance = new Singleton(); } }
DCL模式虽然在一定程度解决了资源消耗、多余的同步、线程安全等问题,但是,它还是在某种情况下会出现失效问题。枚举单例
第一次加载Singleton类并不会初始化instance,只有在第一次调用getInstance()方法时instance才被初始化,因此第一次调用getInstance方法会导致虚拟机加载SingletonHolder类,这种方法不仅能保证线程安全,也能保证单例对象的唯一性,同时也延迟了单例的实例化,所以这是推荐使用的单例模式。
public enum SingletonEnum { INSTANCE; public void doSomething() { System.out.println("do something"); } }
优点:简单容器实现单例
枚举在java中与普通类一样,不仅能有字段,还能够有自己的方法。重要的是枚举实例的创建是线程安全的,并且在任何情况下它都是一个单例。
public class SingletonManager { private static MapobjMap = new HashMap (); public static void regsiterService(String key, Object instance) { if (!objMap.containsKey(key)) { objMap.put(key, instance); } } public static Object getService(String key) { return objMap.get(key); } }
将多种单例注入一个统一的管理类,使用时根据key获取对象对应类型的对象。总结
这种方法是使得我们可以管理多种类型的单例,在使用过程中可以通过统一的接口进行操作,降低了使用成本,也对用户应藏了具体实现,降低了耦合。
单例是运用频率很高的模式,因为客户端没有高并发的情况,选择哪种方式并不会有太大的影响,出于效率考虑,推荐使用DCL和静态内部类实现单例模式。
单例的优点由于单例模式只存在一个实例,减少了内存开销,特别是一个对象需要频繁的创建、销毁时,而且创建或销毁时性能无法优化,单例模式的优势就十分明显。
由于单例模式只生成一个实例,减少了系统的性能开销,当一个对象的产生需要比较多的资源时,可以通过应用启动时直接产生一个单例对象。然后用永久驻留内存的方式来解决。
单例模式可以避免对资源的多重占用,比如文件的读写操作。
单例模式可以在系统中设置安全的访问点,优化和共享资源访问,比如可以设计一个单例类,负责所有数据表的映射处理。
单例的缺点单例模式一般没有接口,扩展困难。
单例对象如果持有Context,容易引发内存泄漏,此时需要传递给单例对象的Context最好是Application Context。
微信公众号:码农修炼之道
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/71431.html
摘要:所以,在版本前,双重检查锁形式的单例模式是无法保证线程安全的。 单例模式可能是代码最少的模式了,但是少不一定意味着简单,想要用好、用对单例模式,还真得费一番脑筋。本文对Java中常见的单例模式写法做了一个总结,如有错漏之处,恳请读者指正。 饿汉法 顾名思义,饿汉法就是在第一次引用该类的时候就创建对象实例,而不管实际是否需要创建。代码如下: public class Singleton...
摘要:即便如此,出于效率考虑,推荐使用双重校验锁和静态内部类单例模式。 概述 单例模式是应用最广的模式之一,在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个全局对象,这样有利于我们协调系统整体的行为。如在一个应用中,应该只有一个ImageLoader实例,这个ImageLoader中又含有线程池、缓存系统、网络请求等,很消耗资源。因此不应该让它构造多个实...
摘要:一基础接口的意义百度规范扩展回调抽象类的意义想不想通过一线互联网公司面试文档整理为电子书掘金简介谷歌求职记我花了八个月准备谷歌面试掘金原文链接翻译者 【面试宝典】从对象深入分析 Java 中实例变量和类变量的区别 - 掘金原创文章,转载请务必保留原出处为:http://www.54tianzhisheng.cn/... , 欢迎访问我的站点,阅读更多有深度的文章。 实例变量 和 类变量...
阅读 1343·2023-04-26 00:35
阅读 2712·2023-04-25 18:32
阅读 3344·2021-11-24 11:14
阅读 770·2021-11-22 15:24
阅读 1416·2021-11-18 10:07
阅读 6466·2021-09-22 10:57
阅读 2772·2021-09-07 09:58
阅读 3564·2019-08-30 15:54