设计模式 单例模式

  • 优点:
  1. 只有一个实例,减少内存开支,减少创建销毁和初始化的性能开销
  2. 可以避免资源的多重占用,一种资源操作只有一个实例进行操作
  3. 提供全局访问点,优化和共享资源访问
  • 缺点:
  1. 单例模式要求自行实例化,并提供单一实例,因此单例对象的扩展只能对其自身进行修改
  2. 单例模式与开闭原则、单一职责原则冲突

饿汉式单例

// 饿汉式单例,类文件初始化过程中完成单例实例化
public class HungrySingleton {
    private static final HungrySingleton instance = new HungrySingleton();

    // static {
    //     instance = new HungryStaticSingleton();
    // }
    
    private HungrySingleton(){}

    public static HungrySingleton getInstance(){return instance;}
}

懒汉式单例

双重校验锁

public class LazyDoubleCheckSingleton {
    private **volatile** static LazyDoubleCheckSingleton instance;

    private LazyDoubleCheckSingleton(){}

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

volatile 防止指令重排序:使INVOKESPECIAL(初始化指令) 先于 PUTSTATIC(引用赋值指令)

 // t = new Test();
 // NEW指令,创建指定类型的对象实例、对其进行默认初始化,并且将指向该实例的一个引用压入操作数栈顶;
 NEW Test
 // invokespecial会消耗掉操作数栈顶的引用作为传给构造器的“this”参数,所以如果我们希望在invokespecial调用后在操作数栈顶还维持有一个指向新建对象的引用,则使用DUP指令复制操作栈顶数据并入栈
 DUP
 // 调用<init>方法,<init>方法为实例方法,需消耗栈顶的一个对象引用
 INVOKESPECIAL Test.<init> ()V
 // 将引用赋值给静态变量Test.t;
 PUTSTATIC Test.t : LTest;

静态内部类

/*
 * 内部静态类实现懒汉式单例
 *
 * 由于java类的加载机制,在加载LaztStaticInnerClassSingleton时,并不会加载LazyHolder类
 * 只有当调用getInstance方法时,才会对LazyHolder进行加载,从而实现懒加载
 */
public class LazyStaticInnerClassSingleton {

    private LazyStaticInnerClassSingleton(){}

    public static LazyStaticInnerClassSingleton getInstance(){
        return LazyHolder.instance;
    }

    private static class LazyHolder{
        private static final LazyStaticInnerClassSingleton instance = new LazyStaticInnerClassSingleton();
    }
}

枚举式单例

// 枚举式单例(饿汉) - 可避免反射破坏 - 枚举类型不允许反射创建实例
public enum EnumSingleton {
    instance;

    // 其实这段方法并没有太大意义,不过习惯使用getInstance表示单例,直接用EnumSingleton.instance也可。
    public static EnumSingleton getInstance(){return instance;}

    // 以下为应用属性和代码
    private int index = 0;

    public void print(){
        System.out.println(index);
    }
}
// 枚举类的实际实现:
  public final static enum Ltop/kiqi/design/pattern/singleton/hungry/EnumSingleton; instance
// synthetic 标记,由编译器生成的属性/方法/类
  private final static synthetic [Ltop/kiqi/design/pattern/singleton/hungry/EnumSingleton; $VALUES

  static{
      instance = new EnumSingleton("instance",0);
      $VALUES = new EnumSingleton[]{instance};
  }

容器式单例(IOC)

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

    private ContainerSingleton(){
        throw new RuntimeException("非法访问!");
    }

    // 方法一: 双重校验锁
    public static Object getInstance(String className){
        if(!ioc.contains(className)){
            synchronized (ContainerSingleton.class){
                if(!ioc.contains(className)){
                    try{
                        Object instance = Class.forName(className).newInstance();
                        ioc.put(className,instance);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
        }
        return ioc.get(className);
    }

    // 方法二: 利用ConcurrentHashMap的putIfAbsent
    public static Object getInstance(String className){
        if(!ioc.contains(className)){
            try {
                ioc.putIfAbsent(className, Class.forName(className).newInstance());
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        return ioc.get(className);
    }
}

线程私有式单例

// 线程级单例 - 使用ThreadLocal为每个线程私有维护一份实例对象
public class ThreadLocalSingleton {
    private static ThreadLocal<ThreadLocalSingleton> threadLocalSingleton = ThreadLocal.withInitial(ThreadLocalSingleton::new);

    private ThreadLocalSingleton(){}

    public static ThreadLocalSingleton getInstance(){
        return threadLocalSingleton.get();
    }
}

避免破坏单例

避免反射破坏单例

 // 构造方法中添加判断
    private Singleton(){
        if(instance != null){
            throw new RuntimeException("非法访问!");
        }
    }

避免反序列化破坏单例:

 // 实现方法 - 当调用java反序列化方法(ObjectInputStream.readObject())时,会判断该方法,如果存在,则使用该方法的返回值。
 public Object readResolve(){
    return instance;
 };
原文地址:https://www.cnblogs.com/kiqi/p/14007606.html