实现单例模式的八种模式:饿汉式,懒汉式,双重检查锁模式,静态内部类模式,序列化模式,注册式之枚举,注册式之容器,线程实现ThreadLocal
参考大神Tom的《Spring 5核心原理与30个类手写实战-谭勇德》
单例模式 Singleton Pattern
确保一个类在任何场景下只有一个实例,并提供一个全局访问点
使用场景
J2EE 标准中的 ServletContext 、 Spring 框架应用中的ApplicationContext 、数据库
的连接池 也都是单例形式
饿汉式
在类加载的时候就立即初始化,并且创建单例对象,属于线程安全
SpringIOC容器ApplicationContext就是典型的饿汉式单例模式
优点: 没有加任何锁、执行效率比较高,用户体验比懒汉式单例模式更好。
缺点: 类加载的时候就初始化,不管用与不用都占着空间,浪费了内存,有可能“占着茅坑不拉屎
package com.example.demo.singleton; */*** ** 饿汉式* *** *@author* *Java4ye* ** @date 2020/9/6 8:19* **/* public class HungrySingleton { private static final HungrySingleton hungry=new HungrySingleton(); private HungrySingleton() { } public static HungrySingleton getInstance(){ return hungry; } } 复制代码
懒汉式
使用时才去创建该对象
package com.example.demo.singleton; */*** ** 懒汉式* *** *@author* *Java4ye* ** @date 2020/9/6 8:19* **/* public class LazySingleton { private static LazySingleton instance=null; private LazySingleton(){} public static LazySingleton getInstance(){ if(instance==null){ instance=new LazySingleton(); } return instance; } } 复制代码
双重检查锁模式
懒汉式是线程不安全的,需要加锁。
package com.example.demo.singleton; */*** ** 双重检查锁* *** *@author* *Java4ye* ** @date 2020/9/6 8:19* **/* public class LazyDoubleCheckSingleton { private static LazyDoubleCheckSingleton lazyDoubleCheckMode=null; private LazyDoubleCheckSingleton(){} public static LazyDoubleCheckSingleton getInstance(){ if (lazyDoubleCheckMode==null){ synchronized(LazyDoubleCheckSingleton.class){ if (lazyDoubleCheckMode==null){ lazyDoubleCheckMode= new LazyDoubleCheckSingleton(); } } } return lazyDoubleCheckMode; } } 复制代码
静态内部类模式
这种形式兼顾饿汉式单例模式的内存浪费问题和 synchronized 的性能问题
特点:
加载静态变量,方法,不包括这个静态内部类
被外部类调用的时候内部类才会加载
package com.example.demo.singleton; */*** ** 静态内部类模式* ** 这种形式兼顾饿汉式单例模式的内存浪费问题和 synchronized 的性能问题* ** 加载静态变量,方法,不包括这个静态内部类* ** 被外部类调用的时候内部类才会加载* *** *** *@author* *Java4ye* ** @date 2020/9/6 9:12* **/* public class LazyInnerClassSingleton { public static LazyInnerClassSingleton getInstance() { return InnerLazyHolder.LAZY; } private static class InnerLazyHolder { private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton(); } */*** ** 防止反射创建* **/* private LazyInnerClassSingleton() { if (InnerLazyHolder.LAZY != null) { throw new RuntimeException("不允许创建多个实例"); } } } 复制代码
序列化模式
必须要实现序列化接口 Serializable
,同时提供这个方法Object readResolve()
package com.example.demo.singleton; import java.io.Serializable; */*** ** 序列化模式* *** *** *@author* *Java4ye* ** @date 2020/9/6 21:59* **/* public class SerializableSingleton implements Serializable { private static final long serialVersionUID = 7018585554862336578L; private SerializableSingleton() { } private static final SerializableSingleton INSTANCE = new SerializableSingleton(); public static SerializableSingleton getInstance() { return INSTANCE; } */*** *objectInputStream中通过这个hasReadResolveMethod去判断有没有该方法,有的话反序列化会去调用该方法* *返回类型必须是Object* **/* private Object readResolve() { return INSTANCE; } } 复制代码
注册式单例模式
注册式单例模式又称为登记式单例模式 就是将每一个实例都登记到某一个地方,使用唯一的标识 。注册式单例模式有两种:一种为枚举式单例,另一为容器式单例模式
枚举式
package com.example.demo.singleton; */*** ** 注册式单例模式又称为登记式单例模式 就是将每一个实例都登记到某一个地方,使用唯一的标识 。注册式单例模式有两种:一种为枚举式单例,另一为容器式单例模式* ** 注册式单例模式之枚举式* *** *@author* *Java4ye* ** @date 2020/9/6 23:18* **/* 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; } } 复制代码
容器式
package com.example.demo.singleton; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; */*** ** 注册式单例模式又称为登记式单例模式 就是将每一个实例都登记到某一个地方,使用唯一* ** 的标识 。注册式单例模式有两种:一种为枚举式单例,另一为容器式单例模式* ** 注册式单例模式之容器式* *** *@author* *Java4ye* ** @date 2020/9/9 7:16* **/* public class ContainerSingleton { private ContainerSingleton(){} private static final 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 (InstantiationException | ClassNotFoundException | IllegalAccessException e) { e.printStackTrace(); } return obj; }else{ return ioc.get(className); } } } } 复制代码
线程单例实现 Thread Local
还有我们熟悉的 ThreadLocal
啦
package com.example.demo.singleton; */*** ** 线程单例实现 Thread Local* ** 确保每一个线程只有一个实例对象* *** *@author* *Java4ye* ** @date 2020/9/9 7:31* **/* public class ThreadLocalSingleton { private ThreadLocalSingleton() { } private static final ThreadLocal<ThreadLocalSingleton> threadLocal = ThreadLocal.withInitial(ThreadLocalSingleton::new); public static ThreadLocalSingleton getInstance(){ return threadLocal.get(); } } 复制代码
测试
先简单测试下这个 【懒汉式线程不安全版本】 和 【反射破坏单例】 这两种模式。。。其他几个写在下篇博客啦🐖
package com.example.demo; import com.example.demo.singleton.LazyInnerClassSingleton; import com.example.demo.singleton.LazySingleton; import org.junit.jupiter.api.Test; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; class SingletonTest { */*** ** 线程调度工厂创建线程* **/* static class ExcutorThread implements ThreadFactory { private String name; private AtomicInteger atomicInteger = new AtomicInteger(1); public ExcutorThread(String name) { this.name = name; } @Override public Thread newThread(Runnable task) { int index = atomicInteger.getAndIncrement(); Thread thread = new Thread(task); String threadName = String.format(name + ":%s", index); thread.setName(threadName); System.out.println(threadName); return thread; } } */*** ** 创建线程池* **/* public static ThreadPoolExecutor getThreadPoolExecutor(String threadFactoryName) { return new ThreadPoolExecutor(10, 20, 1, TimeUnit.MINUTES, new LinkedBlockingDeque<>(10), new ExcutorThread(threadFactoryName)); } */*** ** 懒汉式 线程不安全 测试* **/* @Test void testLazySingleton() { ThreadPoolExecutor lazyPool = getThreadPoolExecutor("LazySingleton"); for (int i = 1; i <= 30; i++) { int finalI = i; lazyPool.execute(() -> System.out.println(String.format(LazySingleton.getInstance() + "[%s]", finalI)) ); } *// lazyMode.shutdown();* } */*** ** 反射破坏单例模式* **/* @Test void testLazyInnerClassSingleton() { Class<LazyInnerClassSingleton> lazyInnerClassModeClass = LazyInnerClassSingleton.class; try { Constructor<LazyInnerClassSingleton> constructor = lazyInnerClassModeClass.getDeclaredConstructor(null); constructor.setAccessible(true); LazyInnerClassSingleton lazyInnerClassInstance = constructor.newInstance(); System.out.println(lazyInnerClassInstance); } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { e.printStackTrace(); } } } 复制代码
- 懒汉式
网络异常,图片无法展示
|
- 反射破坏单例模式
网络异常,图片无法展示
|