饿汉单例模式
简单饿汉单例模式
public class HungrySingleton { private static final HungrySingleton hungrySington = new HungrySingleton(); private HungrySingleton() { } public static HungrySingleton getInstance(){ return hungrySington; } }
静态代码块实现饿汉模式
public class HungryStaticSingleton { private static final HungryStaticSingleton hungrySington; static { hungrySington = new HungryStaticSingleton(); } private HungryStaticSingleton() { } public static HungryStaticSingleton getInstance() { return hungrySington; } }
懒汉单例模式
简单懒汉单例模式(线程不安全)
public class LazySimplySington { private static LazySimplySington lazy; private LazySimplySington() { } //jdk1.8之后对synchronized性能优化不少 //不可避免还是存在一定的性能问题 public synchronized static LazySimplySington getInstance() { if (lazy == null) { lazy = new LazySimplySington(); } return lazy; } }
双重检查实现单例模式(线程安全)
注意指令重排序问题,需要单独加voliate关键字
public class LazyDoubleCheckSington { private volatile static LazyDoubleCheckSington lazy = null; private LazyDoubleCheckSington() { } //jdk1.8之后对synchronized性能优化不少 //不可避免还是存在一定的性能问题 public static LazyDoubleCheckSington getInstance() { if (lazy == null) { synchronized (LazyDoubleCheckSington.class) { if (lazy == null) { lazy = new LazyDoubleCheckSington(); //指令重排序的问题:也就是 第二步和第三步会颠倒, // 解决方式 变量上加voliate,让线程可见 //CPU执行时候会转换成JVM指令执行 //1.分配内存给这个对象 //2.初始化对象 //3.将初始化好的对象和内存地址建立关联,赋值 //4.用户初次始化 } } } return lazy; } }
内部类实现单例模式(线程安全)
public class LazyInnerClassSington { //虽然构造方法有了,但是逃不过反射的法眼 private LazyInnerClassSington() { } //懒汉式单例 //LazyHolder里面的逻辑需要等到外部方法调用时才执行 //巧妙利用了内部类的特性 //JVM底层执行逻辑,完美的避免了线程安全问题 public static final LazyInnerClassSington getInstance(){ return LazyHolder.lazy; } private static class LazyHolder{ private static final LazyInnerClassSington lazy = new LazyInnerClassSington(); } }
暴力破解单例
反射暴力破解
public class LazyInnerClassSingtonTest { public static void main(String[] args) { try { //反射,破坏了单例 Class<?> clazz = LazyInnerClassSington.class; Constructor<?> c = clazz.getDeclaredConstructor(null); c.setAccessible(true); Object o = c.newInstance(); Object o2 = c.newInstance(); System.out.println(o == o2); }catch (Exception e){ e.printStackTrace(); } } }
解决方式:
在构造函数内部添加判断
//虽然构造方法有了,但是逃不过反射的法眼 private LazyInnerClassSington() { if (LazyHolder.lazy != null){ throw new RuntimeException("不允许构建多个实例"); } }
测试:
完整代码:
public class LazyInnerClassSington { //虽然构造方法有了,但是逃不过反射的法眼 private LazyInnerClassSington() { if (LazyHolder.lazy != null){ throw new RuntimeException("不允许构建多个实例"); } } //懒汉式单例 //LazyHolder里面的逻辑需要等到外部方法调用时才执行 //巧妙利用了内部类的特性 //JVM底层执行逻辑,完美的避免了线程安全问题 public static final LazyInnerClassSington getInstance(){ return LazyHolder.lazy; } private static class LazyHolder{ private static final LazyInnerClassSington lazy = new LazyInnerClassSington(); } }
序列化和反序列化暴力破解
来个饿汉单例
//反序列化时导致单例破坏 public class SeriableSingleton implements Serializable { //序列化就是说把内存中的状态通过转换成字节码的形式 //从而转换一个IO流,写入到其他地方(可以是磁盘、网络IO) //内存中状态给永久保存下来了 //反序列化 //讲已经持久化的字节码内容,转换为IO流 //通过IO流的读取,进而将读取的内容转换为Java对象 //在转换过程中会重新创建对象new public final static SeriableSingleton INSTANCE = new SeriableSingleton(); private SeriableSingleton(){} public static SeriableSingleton getInstance(){ return INSTANCE; }
破解
public class SeriableSingletonTest { public static void main(String[] args) { SeriableSingleton s1 = null; SeriableSingleton s2 = SeriableSingleton.getInstance(); FileOutputStream fos = null; try { fos = new FileOutputStream("SeriableSingleton.obj"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(s2); oos.flush(); oos.close(); FileInputStream fis = new FileInputStream("SeriableSingleton.obj"); ObjectInputStream ois = new ObjectInputStream(fis); s1 = (SeriableSingleton)ois.readObject(); ois.close(); System.out.println(s1); System.out.println(s2); System.out.println(s1 == s2); } catch (Exception e) { e.printStackTrace(); } } }
解决方式resolve方法
加个方法:
//序列化解决单例问题的方式 //重写readResolve方法,只不过是覆盖了反序列化出来的对象 //还是创建了两次,发生在JVM层面,相对来说比较安全 //之前反序列化出来的对象会被GC回收 private Object readResolve(){ return INSTANCE; }
完整代码:
//反序列化时导致单例破坏 public class SeriableSingleton implements Serializable { //序列化就是说把内存中的状态通过转换成字节码的形式 //从而转换一个IO流,写入到其他地方(可以是磁盘、网络IO) //内存中状态给永久保存下来了 //反序列化 //将已经持久化的字节码内容,转换为IO流 //通过IO流的读取,进而将读取的内容转换为Java对象 //在转换过程中会重新创建对象new public final static SeriableSingleton INSTANCE = new SeriableSingleton(); private SeriableSingleton(){} public static SeriableSingleton getInstance(){ return INSTANCE; } //序列化解决单例问题的方式 //重写readResolve方法,只不过是覆盖了反序列化出来的对象 //还是创建了两次,发生在JVM层面,相对来说比较安全 //之前反序列化出来的对象会被GC回收 private Object readResolve(){ return INSTANCE; } }
注册类单例
枚举实现单例(推荐)
可以同时实现防止反射和序列化反序列化暴力破解
public enum EnumSingleton { INSTANCE; private String name; public String getData() { return name; } public void setData(String data) { this.name = data; } public static EnumSingleton getInstance(){ return INSTANCE; } }
使用jad工具查看枚举类的反编译代码
反编译代码:
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov. // Jad home page: http://www.kpdus.com/jad.html // Decompiler options: packimports(3) // Source File Name: EnumSingleton.java package com.example.sington.register; public final class EnumSingleton extends Enum { public static EnumSingleton[] values() { return (EnumSingleton[])$VALUES.clone(); } public static EnumSingleton valueOf(String name) { return (EnumSingleton)Enum.valueOf(com/example/sington/register/EnumSingleton, name); } private EnumSingleton(String s, int i) { super(s, i); } public Object getData() { return data; } public void setData(Object data) { this.data = data; } public static EnumSingleton getInstance() { return INSTANCE; } public static final EnumSingleton INSTANCE; private Object data; private static final EnumSingleton $VALUES[]; static { INSTANCE = new EnumSingleton("INSTANCE", 0); $VALUES = (new EnumSingleton[] { INSTANCE }); } }
容器实现单例
public class ContainerSingleton { private ContainerSingleton() { } private static Map<String, Object> ioc = new ConcurrentHashMap<>(); public static Object getBean(String className) { synchronized (ioc){ if (!ioc.containsKey(className)) { Object obj = null; try { obj = Class.forName(className).newInstance(); ioc.put(className, obj); } catch (Exception e) { e.printStackTrace(); } } return ioc.get(className); } } }
public class Pojo { }
public class ContainerSingletonTest { public static void main(String[] args) { try { long start = System.currentTimeMillis(); ConcurrentExecutor.execute(new ConcurrentExecutor.RunHandler() { public void handler() { Object obj = ContainerSingleton.getBean("com.example.Pojo"); System.out.println(System.currentTimeMillis() + ": " + obj); } }, 10,6); long end = System.currentTimeMillis(); System.out.println("总耗时:" + (end - start) + " ms."); }catch (Exception e){ e.printStackTrace(); } } }
线程间实现单例ThreadLocal
public class ThreadLocalSingleton { private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance = new ThreadLocal<ThreadLocalSingleton>() { @Override protected ThreadLocalSingleton initialValue() { return new ThreadLocalSingleton(); } }; private ThreadLocalSingleton() { } public static ThreadLocalSingleton getInstance() { return threadLocalInstance.get(); } }
public class ExectorThread implements Runnable { @Override public void run() { // LazySimplySington instance = LazySimplySington.getInstance(); // LazyDoubleCheckSington instance = LazyDoubleCheckSington.getInstance(); ThreadLocalSingleton instance = ThreadLocalSingleton.getInstance(); System.out.println(Thread.currentThread().getName()+" ---"+instance); ThreadLocalSingleton instance2 = ThreadLocalSingleton.getInstance(); System.out.println(Thread.currentThread().getName()+" ---"+instance2); ThreadLocalSingleton instance3 = ThreadLocalSingleton.getInstance(); System.out.println(Thread.currentThread().getName()+" ---"+instance3); ThreadLocalSingleton instance4 = ThreadLocalSingleton.getInstance(); System.out.println(Thread.currentThread().getName()+" ---"+instance4); ThreadLocalSingleton instance5 = ThreadLocalSingleton.getInstance(); System.out.println(Thread.currentThread().getName()+" ---"+instance5); System.out.println(Thread.currentThread().getName()+" ---"+instance); } }
public class ThreadLocalSingletonTest { public static void main(String[] args) { System.out.println(ThreadLocalSingleton.getInstance()); System.out.println(ThreadLocalSingleton.getInstance()); System.out.println(ThreadLocalSingleton.getInstance()); System.out.println(ThreadLocalSingleton.getInstance()); System.out.println(ThreadLocalSingleton.getInstance()); Thread t1 = new Thread(new ExectorThread()); Thread t2 = new Thread(new ExectorThread()); t1.start(); t2.start(); System.out.println("End"); } }
可以看到线程间是单例的。
单例模式的本质
本质: 控制实例数目。(研磨设计模式)。
笔记和代码地址
代码:
https://github.com/hufanglei/pattern-learn/tree/master/src/main/java/com/example/sington
笔记https://blog.csdn.net/baidu_21349635/article/details/106067581