单例模式使用场景很多:session,上下文,数据库连接池,全局配置文件等。
单例模式的写法也有很多种:
1.饿汉式 : 加载类的时候,就创建对象。线程安全。但有的时候不一定用得着,可能造成内存浪费。
package stu.design.singleton.hungry; public class HungrySingleton { private static final HungrySingleton hungrySingleton = new HungrySingleton(); private HungrySingleton() { } public static HungrySingleton getInstance() { return hungrySingleton; } }
1.2 静态饿汉式:在类中的静态属性中创建类。
package stu.design.singleton.hungry; public class HungryStaticSingleton { private static final HungryStaticSingleton hungrySingleton; static { hungrySingleton = new HungryStaticSingleton(); } private HungryStaticSingleton() { } public static HungryStaticSingleton getInstance() { return hungrySingleton; } }
2.懒汉式: 第一次调用getInstance()方法的时候再创建。 容易造成线程安全问题。
package stu.design.singleton.lazy; public class LazySimpleSingleton { private static LazySimpleSingleton lazy = null; private LazySimpleSingleton() { } public synchronized static LazySimpleSingleton getInstance() { if (null == lazy) { lazy = new LazySimpleSingleton(); } return lazy; } }
2.2 双重检测懒汉式:懒汉式中,使用synchronized 锁加到方法上,影响性能。 可以加锁到执行代码间。
package stu.design.singleton.lazy; public class LazyDoubleCheckSingleton { private volatile static LazyDoubleCheckSingleton lazy = null; private LazyDoubleCheckSingleton() { } public static LazyDoubleCheckSingleton getInstance() { if (null == lazy) { synchronized (LazyDoubleCheckSingleton.class) { if (null == lazy) { lazy = new LazyDoubleCheckSingleton(); } } } return lazy; } }
2.3 内部静态类的单例模式: 内部静态类在类加载的时候,JVM底层实现了实例化。其可能会被序列化破坏,需实现 Serializable 的 readResolve()方法
package stu.design.singleton.lazy; import java.io.Serializable; // static inner class public class LazyInnerClassSingleton implements Serializable { // 防止反射破坏单例模式 private LazyInnerClassSingleton() { if (null != LazyHolder.lazy) { throw new RuntimeException("not allow many instance"); } } public static final LazyInnerClassSingleton getInstance() { return LazyHolder.lazy; } private static class LazyHolder { private static final LazyInnerClassSingleton lazy = new LazyInnerClassSingleton(); } // 防止序列化破坏单例模式 private Object readResolve(){ return LazyHolder.lazy; } }
3.枚举实现: 枚举的优势在于JVM底层避免了枚举类型被反射,序列化破坏其单例性。
package stu.design.singleton.register; 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; } }
3.2 容器实现: 容器中线程不安全,容器使用个的是每个线程作为主键。 所以是每个线程是安全,但线程间不是同一单例。
package stu.design.singleton.register; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; 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 obj; } return ioc.get(className); } } }
4.伪线程安全实现:每个线程的单例是安全的,但线程与线程间的不是安全的。
package stu.design.singleton.threadlocal; /** * 伪线程安全 * 使用threadLocal,多数据源动态切换 * data source route */ public class ThreadLocalSingleton { private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance = new ThreadLocal<ThreadLocalSingleton>() { @Override protected ThreadLocalSingleton initialValue() { return new ThreadLocalSingleton(); } }; public static ThreadLocalSingleton getInstance() { return threadLocalInstance.get(); } }
推荐使用枚举实现。
欢迎指正。