多种单例模式实现方法详解——java代码

来源:互联网 发布:下载软件管理器 编辑:程序博客网 时间:2024/05/20 20:03
单例模式大致可以归纳为以下集中实现方式:简单懒汉模式、简单饿汉模式、同步懒汉模式、双重检验懒汉模式、静态内部类懒汉模式、枚举模式;
以上各个单例之间的关系可以归纳如下,我们假设getInstance()方法为获得实例的接口:
简单懒汉模式是很多初学者最快会想到的办法。
简单懒汉模式代码:
public class SingleInstance {    private static SingleInstance instance;    private SingleInstance() {}    public static SingleInstance getInstance() {        if (instance == null) {            instance = new SingleInstance();        }        return instance;    }}


简单懒汉模式存在问题:假设以下情况,线程A、B同时调用getInstance(),且同时到达if (instance == null),则此时两个线程都可以进入该if语句的代码,则会产生两个实例(假设先到的线程是A,A产生了一个实例放在instance中,然后返回,随后B也产生了一个实例放在instance中,把A产生的实例覆盖掉了,然后返回,则AB两个线程拿到了两个不同的实例),因此存在同步问题。

有两个方向可解决该问题,改进懒汉模式或直接使用饿汉模式:

简单懒汉模式—(解决同步问题)—>同步懒汉模式
同步懒汉模式代码:
public class SingleInstance {    private static SingleInstance instance;    private SingleInstance() {}    public static synchronized SingleInstance getInstance() {        if (instance == null) {            instance = new SingleInstance();        }        return instance;    }}


没错就是加了一个syn关键字,变成了同步的方法,同步懒汉模式存在的问题:假定以下情况,当instance为空时,线程A、B同时调用getInstance(),为避免产生两个实例,此时同步是需要的,但是当instance已经存在,A、B两个线程同时调用getInstance()时,必须等其中一个线程调用完,第二个线程才能调用,此时这个同步是不需要的,因为instance存在的情况下,if (instance == null)当中的代码将不会执行,即不会出现同步问题,A和B线程可以同时调用getInstance,但是因为syn无法同时调用,严重影响效率;

简单懒汉模式—(解决同步问题)—>简单饿汉模式
简单饿汉模式代码:
public class SingleInstance {    private static SingleInstance instance = new SingleInstance();    private SingleInstance() {}    public static synchronized SingleInstance getInstance() {        return instance;    }}


简单饿汉模式:在类加载的时候就会初始化需要的实例,避免了同步问题,同时保证了有且只有一个实例,但是类的加载完成到类真正的被使用还有一段时间,理论上这段时间内,instance存不存在无所谓,因为不会被调用,而在饿汉模式中instance自类被加载就会一直存在,在内存中占据位置,会影响效率(实际开发中如果需要的实例不是特别巨大的话,这种方式是一个可行的方式);

同步懒汉模式—(解决效率问题)—>双重检验懒汉模式
双重检验懒汉模式代码如下:
public class SingleInstance {    private static SingleInstance instance;    private SingleInstance() {}    public static SingleInstance getInstance() {        if (instance == null) {            synchronized (SingleInstance.class) {                if (instance == null) {                    instance = new SingleInstance();                }            }        }        return instance;    }}


双重检验懒汉模式解决了效率问题,当instance不存在时,假设A、B同时调用getInstance(),会发生阻塞,先调用同步块中的内容的线程会生产一个实例,后调用同步块中的内容的线程在同步块中因为第二个 if (instance == null)不符合转了个圈就走了,当instance存在时,因为第一个 if (instance == null)不符合,因此不会进入同步块,也就不会有阻塞的问题,这个逻辑之下,第一次新建instance时可能会阻塞,随后调用的时候都不会阻塞,同时兼顾了同步和效率问题。可能存在的问题即JVM对代码重排序,这一点我们暂且不表。

简单饿汉模式—(解决存储效率问题)—>静态内部类饿汉模式
静态内部饿汉模式如下:
public class SingleInstance {    private static class Instance{        private static SingleInstance instance = new SingleInstance();    }    private SingleInstance() {}    public static SingleInstance getInstance() {        return Instance.instance;    }}


静态内部类饿汉模式利用静态内部类的特点——当被使用是,才会被加载、初始化,成功的在getInstance()第一次被调用时加载Instance并初始化instance,解决了过早产生instance影响存储效率的问题,同时因为instance是静态变量,不会有第二个产生,保证了是单例模式。该方法可能有的问题和以上所有方法实现单例子是一样的,即可能通过反射来破解单例的限制,为了保证不被反射攻击,在相关书籍中出现了基于枚举的单例,因对基于枚举的单例不太熟悉,回头再补。
所以总结一下,各个实现单例的方式之间有如下关系:
简单懒汉模式—(解决同步问题)—>同步懒汉模式—(解决效率问题)—>双重检验懒汉模式
—(解决同步问题,或者说用饿汉模式就不存在同步问题,其实和懒汉模式并没有什么先后关系)—>简单饿汉模式—(解决存储效率问题)—>静态内部类饿汉模式
—(解决反射问题)—>基于枚举单例
原创粉丝点击