Java设计模式新解之单例模式

一、定义

单例是一种创建型设计模式, 让你能够保证一个类只有一个实例, 并提供一个访问该实例的全局节点。

二、适用场景

    1.需要频繁实例化然后销毁的对象。 
    2.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。 
    3.有状态的工具类对象。 
    4.频繁访问数据库或文件的对象。

经典使用场景:

    1.资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如日志文件、应用配置等。 
    2.控制资源的情况下,方便资源之间的相互通信。如线程池、数据库连接池等。 

三、单例的几种实现方式

1、饿汉式

// 饿汉式(静态变量的方式)
public class Singleton {

    private static final Singleton instance = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return instance;
    }

}

// 饿汉式(静态代码块的方式)
public class Singleton {
	
	private Singleton() {		
	}
	
	private  static Singleton instance;
	
	static {
		instance = new Singleton();
	}
	
	public static Singleton getInstance() {
		return instance;
	}

}

优缺点说明:

优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
缺点:在类装载的时候就完成实例化,没有达到懒加载的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。

2、懒汉式

public class Singleton {

    private static Singleton instance;

    private Singleton() {
    
    }

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

优缺点说明:

(1)起到了懒加载的效果,但是只能在单线程下使用。
(2)如果在多线程下,一个线程进入了if (singleton == null) 判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用此种方式。

3、懒汉式加锁

public class Singleton {

    private static Singleton instance;

    private Singleton() {

    }

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

优缺点说明:
(1)解决了线程不安全问题
(2)效率太低了,每个线程在想获得类的实例的时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行了。方法同步效率太低,不建议使用。


4、Double-Check(双重校验锁)

public class Singleton {

    private static Singleton instance;

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

缺点:这种方式,会发生空指针异常。比如,当线程一获取锁之后,正在创建对象(创建对象需要一系列步骤,编译器优化会发生指令重排序),当线程一创建对象后,但对象的一些属性并没有初始化结束。此时线程二到了条件判断,此时对象并不为空,但是对象的一些属性的初始化并没有结束,如果此时用线程二去访问该对象的一些属性,可能会造成空指针异常。解决这个问题,我们可以把对象定义成 volatile 。volatile 的作用是禁止JVM进行指令重排序。

5、Double-Check 加 volatile

public class Singleton {

    private static volatile Singleton instance;

    private Singleton() {
     
    }

    public static Singleton getInstance() {

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

到了这一步,对象终于可以安全的创建了,也不会有什么问题了。

优缺点说明:
(1) Double-Check概念是多线程开发中经常使用到的,如代码中所示,我们进行了两次if (singleton == null)检查,这样就可以保证线程安全了。

(2)这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton == null)不成立,直接return实例化对象,也避免了反复进行方法的同步。
(3)线程安全,延迟加载,效率较高。实际开发中可以使用。

6、静态内部类

public class Singleton {

    private Singleton() {

    }

    private static class SingletonHolder {
        private final static Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}

优缺点说明:
(1)这种方式利用了类装载的机制来保证初始化实例时只有一个线程。
(2)静态内部类方式在Singleton类被装载时,内部类并不会立即实例化,而是在调用getInstance方法,才会装载内部类类,而且只调用一次,从而完成Singleton的实例化。
(3)类的静态属性只会在第一次加载类的时候才会初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
(4)避免了线程不安全,利用静态内部类的特点来实现延迟加载,效率高。推荐使用。

7、枚举

public class Singleton {
    private Singleton() {

    }

    private enum SingletonObject {
        INSTANCE;

        private final Singleton instance;

        SingletonObject() {
            instance = new Singleton();
        }

        public Singleton getInstance() {
            return instance;
        }
    }

    public static Singleton getInstance() {
        return SingletonObject.INSTANCE.getInstance();
    }
}

优缺点说明:

目前枚举方式是实现单例性能最好,也是最安全的一种方式。

四、总结

单例模式中,公用方法getInstance()方法都是静态的(static),实例和构造方法又都是私有的(private)。

以上分析了各种实现方式的优缺点,实际开发中,建议使用后三种方式:Double-Check加volatile、静态内部类和枚举。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值