java中单例模式的几种写法

#什么是单例模式
单例模式是使用的最广泛的设计模式之一,用来确保一个类对外只提供唯一的实例,比如连接池、缓存、日志对象的实现基本都用的单例模式。实现单例的方法主要有以下几种,每种都有各自的优缺点,为了实现真正的单例,我们可以根据实际需求,选择合适的方法。
##1. 饿汉模式

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

优点: 简单,线程安全
缺点: 不能延时加载
推荐指数: 4星
说明: 类初始化时就加载静态变量,创建了实例,不存在多线程问题,缺点是不能在实际使用的时候才创建对象

##2. 懒汉模式

  • 线程不安全
public class Singleton {
    private static Singleton instance;
    private Singleton() {}

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

优点: 延迟加载
缺点:线程不安全
推荐指数: 2星
说明:在多线程环境下,很可能产生多个实例。

  • 线程安全
public class Singleton {
    private static Singleton instance;
    private Singleton() {}

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

优点: 延迟加载,线程安全
缺点:性能较低
推荐指数: 3星
说明:保证了线程安全,但是由于是直接对方法进行同步,会降低性能,实际上只有在初次调用此方法时,才需要加锁来确保线程安全,一旦设置好instance实例之后,就不需再同步该方法了,也就是说,除了第一次,以后每次调用该方法,同步都是多余的

##3. 双重检查锁(DCL)

public class Singleton {
    private volatile static Singleton instance;//volatile可防止指令重排
    private Singleton() {}

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

优点: 延迟加载,线程安全,性能较高
缺点:较为繁琐,不适用于jdk1.5以下的版本
推荐指数: 4星
说明:该方法是对懒汉模式的改进,既保证了线程安全,又不是对整个方法进行同步,对性能基本没有什么影响。不足之处是需要使用volatile关键字来保证有序性,在创建对象时有三个步骤:
1、分配内存空间。
2、初始化对象。
3、将内存空间的地址赋值给对应的引用。
如果指令重排,导致实际创建对象的顺序变成
1、分配内存空间。
2、将内存空间的地址赋值给对应的引用。
3、初始化对象。
那么就有可能暴露一个未初始化的不可靠对象,而jdk1.5上的版本可以使用volatile关键字有效避免指令重排的问题,从而保证有序性。具体可以参考这篇文章:Java 并发编程:volatile的使用及其原理

##4. 枚举

public enum Singleton {
    INSTANCE;
	
    public void doSomething() {}
}

优点:代码简洁, 线程安全
缺点:非延迟加载
推荐指数: 5星
说明:通过枚举方式来创建单例是《Effective Java》作者Joshua Bloch大力推荐的,因为它有以下几点优势:

  1. 无法通过反射调用创建实例,而在其他类型中,即使是私有方法,也能通过反射,创建新的实例。查看 java.lang.reflect.Constructor的newInstance()方法中有如下代码,说明枚举类型是禁止了通过反射构造枚举对象的
  if ((clazz.getModifiers() & Modifier.ENUM) != 0)   
     throw new IllegalArgumentException("Cannot reflectively      create enum objects");

2.可以有效的防止反序列化,以下是java.lang.Enum类的代码,可以看出,enum类型无法被反序列化

    private void readObject(ObjectInputStream in) throws IOException,
        ClassNotFoundException {
        throw new InvalidObjectException("can't deserialize enum");
    }

    private void readObjectNoData() throws ObjectStreamException {
        throw new InvalidObjectException("can't deserialize enum");
    }

3 枚举实例无法被克隆,以下是java.lang.Enum代码,如果要强行进行clone,直接抛出异常

    protected final Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }

##5. 静态内部类

 public class Singleton{
    private Singleton(){}

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

优点: 线程安全,延迟加载
缺点:
推荐指数: 5星
说明:该方式也称为IoDH( Initialization Demand Holder),可以说是结合了饿汉模式和懒汉模式的优点,既实现了延迟加载,也保证了线程安全,建议优先采用该方式创建单例。

参考:

  1. wiki-单例模式
  2. 单例模式方法大全
  3. 单例模式的七种写法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值