关于单例模式,需要解决三个方面的问题
(1)线程安全问题。
(2)性能问题。
(3)懒加载(资源占有问题)。
关于下面几种方式的缺点,除了DCL,HOLDER和枚举单例模式其他只做简单描述,具体可自行了解。
懒汉式:锁粒度过大,影响性能,串行执行影响效率,会引起阻塞。
public class Lazy {
private static Lazy lazy = null;
private Lazy(){}
public synchronized static Lazy getInstance(){
if(null == lazy){
lazy = new Lazy();
}
return lazy;
}
}
饿汉式:无懒加载
public class Hungry {
private static Hungry hungry = new Hungry();
private Hungry(){}
public static Hungry getInstance(){
return hungry;
}
}
DCL(double-check-locking)
public class DCL {
private static volatile DCL dcl = null;
private DCL(){}
public static DCL getInstance(){
if(null == dcl){
synchronized (DCL.class){
if(null == dcl){
dcl = new DCL();
}
}
}
return dcl;
}
}
由于懒汉式锁粒度太大,考虑把锁加在代码块上,但是如果同时两个线程访问getInstance(),线程1获取锁,线程2做了1的判断在等待,此时如果线程1创建了dcl对象,那么如果不加3的话会重复创建,所以3必须要加,这种方式的单例模式被称为双重加锁模式。
步骤3,非原子性 分三步:1、为对象分配内存空间。2、创建对象。3、对象指向内存空间,2,3有可能重排序,但是不影响结果,第三步进行之后对象将不为null,所以如果2,3重排序,先执行3,第一个判断不为空,会返回一个对象,但实际对象还未创建,会返回NULLPOINTEXCEPTION,这就是指令重排给我们带来的影响,所以加volatail,防止指令重排。
Holder模式
public class Holder {
private static class Hold{
private static Holder holder = new Holder();
}
public static Holder getInstance(){
return Hold.holder;
}
}
创建一个获取对象的静态内部类,它里面的holder实例对象不会在类加载的时候被创建,只在被调用时被创建一次,满足所有要求。
枚举类模式(枚举+Holder模式)
public class EnumSingleton {
private EnumSingleton(){}
private enum EnumSlt{
INSTANCE;
private EnumSingleton enumSingleton = null;
EnumSlt(){
enumSingleton = new EnumSingleton();
}
}
public static EnumSingleton getInstance(){
return EnumSlt.INSTANCE.enumSingleton;
}
}
类似Holder模式的静态内部类,首先枚举类的构造方法是私有的,这就保证不会被其他方法通过构造器new出对象,再来INSTANCE就是这个枚举类的对象,会在调用的时候被实例化,这样保证了懒加载,在实例化的时候会调用构造器,创建instance对象,因为枚举类中的对象都是static final 修饰的,所以instance对象也只会被实例化一次,这样就保证了线程安全性,因为没加锁,所以性能也相对较高。
基于ThreadLocal实现的单例模式
public class ThreadLocalSingleton {
private static final ThreadLocal<ThreadLocalSingleton> th = new ThreadLocal() {
@Override
protected ThreadLocalSingleton initialValue() {
return new ThreadLocalSingleton();
}
};
private ThreadLocalSingleton() {}
public static ThreadLocalSingleton getInstance() {
return th.get();
}
}