创建型模式--单例模式

一、定义

        单例模式,一个类保证全局唯一性,只能创建该类型一个对象,并提供一个全局访问点。

        缺点:

        1)没有接口,扩展困难,违反开闭原则;

        2)如果扩展单例对象,需要修改代码。

二、类图

三、四种实现

1.饿汉式

1)直接new出来

public class HungrySingleton {
    //直接new出来,全局唯一性
    private static HungrySingleton instance = new HungrySingleton();
    //防止被new出来,但可以通过反射创建
    private HungrySingleton(){}
    //全局唯一访问入口
    public static HungrySingleton getInstance(){
        return instance;
    }
}

2)通过static块,在类加载的时候初始化实例

public class HungryStaticSingleton {
    private static final HungryStaticSingleton instance;
    
    //通过static块,在类加载的时候初始化实例
    static  {
        instance = new HungryStaticSingleton();
    }
    private HungryStaticSingleton(){}
    public static HungryStaticSingleton getInstance(){
        return instance;
    }
}

2.懒汉式

1)线程不安全

public class LazySimpleSingleton {
    private static LazySimpleSingleton instance;

    private LazySimpleSingleton(){}
    //线程不安全
    public static LazySimpleSingleton getInstance(){
        if(instance == null) {
            instance = new LazySimpleSingleton();
        }
        return instance;
    }
}

2)使用锁使得线程安全,但效率低

public class LazySimpleSingleton {
    private static LazySimpleSingleton instance;

    private LazySimpleSingleton(){}
    //线程不安全
    public static synchronized LazySimpleSingleton getInstance(){
        if(instance == null) {
            instance = new LazySimpleSingleton();
        }
        return instance;
    }
}

3)双重检查锁单例

        特点:线程安全,效率提升,缺点可读性差,不够优雅。

/**
 * 双重检查锁单例
 * 优点:性能提高,线程安全
 * 缺点: 可读性难度加大,不够优雅
 */
public class LazyDoubleCheckSingleton {
    //volatile防止指令重排序
    private static volatile LazyDoubleCheckSingleton instance;

    private LazyDoubleCheckSingleton(){}

    public static LazyDoubleCheckSingleton getInstance(){
        if(instance == null) {
            //synchronized加锁,解决可见性问题
            synchronized(LazyDoubleCheckSingleton.class) {
                if(instance == null) {
                    //指令会重排(三步:创建对象引用;申请内存空间;对象引用指向该内存空间;)
                    instance = new LazyDoubleCheckSingleton();
                }
            }
        }
        return instance;
    }

4)静态内部类

特点:提升了效率和解决了线程安全问题,同时实现方式优雅。

/**
 * 静态内部类实现单例:
 * 优点:写法优雅,利用了Java本身的语法特点,性能高,避免内存浪费。
 * 缺点:反射可以破坏。
 */
public class LazyStaticInnerClassSingleton {
    private LazyStaticInnerClassSingleton(){}

    public LazyStaticInnerClassSingleton getInstance(){
        return InnerClass.INSTANCE;
    }
    private static class InnerClass{
        private static LazyStaticInnerClassSingleton INSTANCE = new LazyStaticInnerClassSingleton();
    }
}

3.注册式

1)Enum注册

public enum EnumSingleton {
    INSTANCE;
    private Object value;

    public Object getValue() {
        return value;
    }

    public void setValue(Object value) {
        this.value = value;
    }

    public static EnumSingleton getInstance() {
        return INSTANCE;
    }
}

特点:通过Enum本身的java语法特性,底层有Map注册声明的对象,在类加载的时候对声明对象进行实例化,注册Map容器中。在实例化的过程中不能通过反射实例化Enum类,可以在Constructor类中找到对应代码。

public T newInstance(Object ... initargs)
    ...
  //不能为Modifier.ENUM类,否则直接抛出throw new IllegalArgumentException("Cannot reflectively create enum objects")异常。
  
  if ((clazz.getModifiers() & Modifier.ENUM) != 0)
      throw new IllegalArgumentException("Cannot reflectively create enum objects");
  ConstructorAccessor ca = constructorAccessor;   // read volatile
  if (ca == null) {
      ca = acquireConstructorAccessor();
  }
}

2)IoC容器注册

public class ContainerSingleton {
    private  ContainerSingleton(){}
    private static volatile Map<String, Object> ioc = new ConcurrentHashMap<>();

    public static Object getInstance(String className){
        if(!ioc.containsKey(className)) {
            try {
                Object o = Class.forName(className).newInstance();
                ioc.put(className, o);
            }catch(Exception e) {

            }
        }
        return ioc.get(className);
    }
    //双重检查锁实现 
    public static Object getInstance2(String className){
        if(!ioc.containsKey(className)) {
            synchronized (ContainerSingleton.class) {
                if(!ioc.containsKey(className)) {
                    try {
                        Object o = Class.forName(className).newInstance();
                        ioc.put(className, o);
                    } catch (Exception e) {

                    }
                }
            }
        }
        return ioc.get(className);
    }
}

特点:容器式单例,是将已经创建的单例对象放入一个ConcurrentHashMap容器中,去获取单例对象的时候判断容器中是否存在,思想还是懒汉式思想,只不过加了容器概念,有容器概率后,就适合创建不同的单例对象的场景,但是思想是懒汉式的话,也会有线程安全问题,需要加锁解决(例如用双重检查锁实现)!

4.ThreadLocal单例

public class ThreadLocalSingleton {
    private static final ThreadLocal<ThreadLocalSingleton> threadLocalSingletonInstance =
            new ThreadLocal<ThreadLocalSingleton>(){
                @Override
                protected ThreadLocalSingleton initialValue() {
                    return new ThreadLocalSingleton();
                }
            };
    private ThreadLocalSingleton(){}
    public static ThreadLocalSingleton getInstance(){
        return threadLocalSingletonInstance.get();
    }
}

四、常见例子

ServletContext、ServletConfig、ApplicationContext、DBPool等实现

五、反射、序列化破坏单例

     (1)反射除了Enum不能破坏之外,其它都能被反射破坏单例。

     (2)序列化和反序列化过程中需要序列化反序列化对象类内写readResolve()方法对实例化单例进行返回,那么反序列化返回和内存中是同一个对象,其实就是创建了两份。

public class SeriableSingleton implements Serializable {
    private final static SeriableSingleton INSTANCE = new SeriableSingleton();
    private SeriableSingleton(){}
    public static SeriableSingleton getInstance() {
        return INSTANCE;
    }
    
    public Object readResolve(){
        return INSTANCE;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值