Java设计模式 ------ 单例模式

单例模式

饿汉式

普通饿汉式

public class HungrySingleton {

    // 不加final 可能会通过反射机制覆盖
    private static final HungrySingleton HUNGRY_SINGLETON = new HungrySingleton();

    // 1. 构造方法私有
    private HungrySingleton() {
    }

    // 2. 全局访问
    public static HungrySingleton getInstance() {
        return HUNGRY_SINGLETON;
    }
}

静态代码块

public class HungryStaticSingleton {

    private static final HungryStaticSingleton HUNGRY_SINGLETON;

    static {
        HUNGRY_SINGLETON = new HungryStaticSingleton();
    }

    // 1. 构造方法私有
    private HungryStaticSingleton() {
    }

    // 2. 全局访问
    public static HungryStaticSingleton getInstance() {
        return HUNGRY_SINGLETON;
    }
}

序列化单例

public class SerializableSingleton implements Serializable {

    public final static SerializableSingleton INSTANCE = new SerializableSingleton();

    public static SerializableSingleton getInstance() {
        return INSTANCE;
    }

    // 序列化破坏单例的解决方式
    // 重写readResolve 方法,只不过是覆盖了反序列化出来的对象
    // 还是创建了两次,发生在JVM层面,相对来说比较安全
    // 之前反序列化出来的对象会被GC回收
    private Object readResolve() {
        return INSTANCE;
    }
}

readResolve()

public class SerializableSingletonTest {

    public static void main(String[] args) {
        SerializableSingleton s1 = null;
        SerializableSingleton s2 = SerializableSingleton.getInstance();

        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream("SerializableSingleton.obj");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(s2);
            oos.flush();
            oos.close();

            FileInputStream fis = new FileInputStream("SerializableSingleton.obj");
            ObjectInputStream ois = new ObjectInputStream(fis);
            s1 = (SerializableSingleton) ois.readObject();
            ois.close();

            System.out.println(s1 == s2);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

懒汉式

存在线程问题的懒汉式

public class LazySingleton {

    private static LazySingleton lazySingleton = null;

    // 构造方法私有化
    private LazySingleton() {
    }

    public static LazySingleton getInstance() {
        if (lazySingleton == null) {
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }
}

方法加synchronized的懒汉式(类锁)

public class LazySimpleSingleton {

    private static LazySimpleSingleton lazySimpleSingleton = null;

    // 构造方法私有化
    private LazySimpleSingleton() {
    }

    // synchronized 性能问题
    public synchronized static LazySimpleSingleton getInstance() {
        if (lazySimpleSingleton == null) {
            lazySimpleSingleton = new LazySimpleSingleton();
        }
        return lazySimpleSingleton;
    }
}

synchronized 加载 static修饰的方法时,会变成类锁。
类锁 是锁住整个类,当有多个线程来访问getInstance()时候将会被阻塞,直到拥有这个类锁的对象被销毁或者主动释放了类锁。

synchronized修饰普通方法,即为 对象锁
多个线程访问同一个对象实例的这个方法时,是会同步的,并且只有一个线程执行完,另一个线程才会执行

双重锁检查

改良后的
对象锁 --> 类锁

public class LazyDoubleCheckSingleton {

    private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton = null;

    // 构造方法私有化
    private LazyDoubleCheckSingleton() {
    }

    // 双重检查锁
    public synchronized static LazyDoubleCheckSingleton getInstance() {
        if (lazyDoubleCheckSingleton == null) {
            synchronized (LazyDoubleCheckSingleton.class) {
                if (lazyDoubleCheckSingleton == null) {
                    lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton();
                    // CPU执行时候会转换成JVM指令
                    // 2 3 会有指令重排序
                    // 1. 分配内存给这个对象
                    // 2. 初始化对象
                    // 3. 将初始化好的对象和内存地址建立关联, 赋值
                    // 4. 用户初次访问
                }
            }
        }
        return lazyDoubleCheckSingleton;
    }
}

lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton()这句,这并非是一个原子操作,事实上在 JVM 中这句话大概做了下面 3 件事情。

  1. lazyDoubleCheckSingleton 分配内存
  2. 调用 LazyDoubleCheckSingleton 的构造函数来初始化成员变量
  3. lazyDoubleCheckSingleton对象指向分配的内存空间(执行完这步 lazyDoubleCheckSingleton 就为非 null 了)

volatile起到禁止指令重排的作用,在它赋值完成之前,就不会调用读操作(lazyDoubleCheckSingleton== null)

静态内部类

静态内部类不会在单例加载时加载,当调用 getInstance() 方法时才会进行加载,达到类似懒汉式效果,并且也是线程安全的。

// 全程没有用到synchronized关键字
// 内部类比外部类优先加载
// 性能最优的一种写法
public class LazyInnerClassSingleton {

    // 虽然构造方法私有,但是逃不过构造方法
    private LazyInnerClassSingleton() {
        if (LazyHolder.LAZY != null) {
            throw new RuntimeException("不允许构建多个实例");
        }
    }

    // 懒汉式单例
    // LazyHolder 里面的逻辑需要等到外部方法调用时才执行
    // 巧妙利用了内部类的特性
    // JVM底层执行逻辑,完美地避免了线程安全问题
    public static final LazyInnerClassSingleton getInstance() {
        return LazyHolder.LAZY;
    }

    private static class LazyHolder {
        private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
    }
}

破坏单例

public class LazyInnerClassSingletonTest {

    public static void main(String[] args) {

        try {
            // 破坏了单例
            Class<?> clazz = LazyInnerClassSingleton.class;
            Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(null);
            // 授权 强制访问
            declaredConstructor.setAccessible(true);
            Object o1 = declaredConstructor.newInstance();

            Object o2 = LazyInnerClassSingleton.getInstance();

            System.out.println(o1 == o2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

枚举

// 懒汉式线程安全
public enum EnumSingleton {
    INSTANCE;

    private Object data;

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

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

能防止反序列化时重新创建新的对象
EnumSingleton的定义利用的enum是一种特殊的class。代码中的第一行INSTANCE会被编译器编译为EnumSingleton本身的一个对象.当第一次访问EnumSingleton.INSTANCE时会创建该对象,并且enum变量的创建是线程安全的.

ThreadLocal

ThreadLocal<>为每一个线程提供一个独立的变量副本

public class ThreadLocalSingleton {

    private ThreadLocalSingleton() {
    }

    private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance =
            ThreadLocal.withInitial(() -> new ThreadLocalSingleton());

    private static ThreadLocalSingleton getInstance() {
        return threadLocalInstance.get();
    }
}

容器化单例

public class ContainerSingleton {
    private ContainerSingleton() {
    }

    private static Map<String, Object> ioc = new ConcurrentHashMap<String, Object>();

    public static Object getBean(String className) {
        if (!ioc.containsKey(className)) {
            Object obj = null;
            try {
                obj = Class.forName(className).newInstance();
                ioc.put(className, obj);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return obj;
        }
        return ioc.get(className);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章