深入讨论Java中的单例模式

    单例模式作为最简单的设计模式之一,其基本思想是保证一个类仅有一个实例,并提供一个访问它的全局访问点。
    主要解决的,是当想控制实例数目,节省系统资源时,一个全局使用的类频繁的创建与销毁问题。
    

    单例模式虽然简单,但是在Java中,细节实现还是有一些要注意的地方

实现方式

1. 懒汉式

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
  
    public static Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}  

上述实现方式最大的问题是不支持多线程,应为没有加锁synchronized。这么设计的主要目的是实现lazy loading

在多线程环境下,要对上述代码进行以下修改:

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
} 

懒汉式的优点是lazy loading,在第一次调用才初始化,避免了内存浪费

缺点是必须加锁synchronized才能保证单例,加锁会影响执行效率

2. 饿汉式

public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return instance;  
    }  
}   

饿汉式的好处是没有加锁,执行效率会提高。缺点是类加载时就初始化,会造成内存浪费。

3. 双检索/双重校验检索(DCL,即double-checked locking)

      这种方式采用双锁机制,安全且在多线程情况下能保持性能。

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton(); //1 
        }  
        }  
    }  
    return singleton;  
    }  
}  

然而在实际的应用中,依然会存在问题。

简单来说,就是在上述代码//1行中,在Singleton构造函数执行前,变量instance可能变为非null的。

因为在创建对象的过程中,线程1可能会存在先分配内存但尚未初始化的情况,造成了返回的instance非空,此时线程2访问代码块时会返回这个尚未执行构造方法的instance引用。因此这里变量使用volatile保证顺序一致性和可见性就会非常重要。但是依然与JVM实现有关。

具体讨论可见一下参考文献:https://blog.csdn.net/chenchaofuck1/article/details/51702129

4. 登记式/静态内部类

这种方式是对饿汉式方式的一种优化,做到了只有在显示调用getInstance时才会创建新实例而不是在类加载时立即 创建,从而节省了内存资源

public class Singleton {  
    private static class SingletonHolder {  
    private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
    return SingletonHolder.INSTANCE;  
    }  
}

5. 枚举式

    以上介绍的方式其实并不能真正实现单例模式,因为Java中存在反射机制,可以在运行时通过反射执行private构造方法创建对象。而枚举不存在这种问题。

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}  
    这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值