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

单例模式大致可以归纳为以下集中实现方式:简单懒汉模式、简单饿汉模式、同步懒汉模式、双重检验懒汉模式、静态内部类懒汉模式、枚举模式;
以上各个单例之间的关系可以归纳如下,我们假设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是静态变量,不会有第二个产生,保证了是单例模式。该方法可能有的问题和以上所有方法实现单例子是一样的,即可能通过反射来破解单例的限制,为了保证不被反射攻击,在相关书籍中出现了基于枚举的单例,因对基于枚举的单例不太熟悉,回头再补。
所以总结一下,各个实现单例的方式之间有如下关系:
简单懒汉模式—(解决同步问题)—>同步懒汉模式—(解决效率问题)—>双重检验懒汉模式
—(解决同步问题,或者说用饿汉模式就不存在同步问题,其实和懒汉模式并没有什么先后关系)—>简单饿汉模式—(解决存储效率问题)—>静态内部类饿汉模式
—(解决反射问题)—>基于枚举单例
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值