单例模式
Meteor
2023-10-25 17:00:16
Categories:
设计模式
Tags:
单例模式
简介
单例模式:一个类在任何时候只有一个实例
单例模式的好处很明显,只有一个实例,节约了空间,只需要创建一次,也节约了时间,缺点就是没有抽象类,无法扩展
源码:singleton
下面展示单例模式的六种实现方式,其中所有实现方式的构造方法都设置成private的,只能通过指定方法才能获取实例对象
饿汉式
饿汉式,将实例设置成静态变量,只要类加载就实例化,JVM在执行实例化指令时会自动保证线程安全,实现简单,但是浪费空间,同时可以利用反射破坏单例
SingletonHungry
package com.meteor.design;
public class SingletonHungry { private SingletonHungry() {
}
private final static SingletonHungry instance = new SingletonHungry();
public static SingletonHungry getInstance() { return instance; } }
|
测试
测试饿汉式单例,尝试用反射破坏单例
public class AppTest{ @Test public void testSingletonHungry() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { SingletonHungry instance = SingletonHungry.getInstance();
Constructor<SingletonHungry> constructor = SingletonHungry.class.getDeclaredConstructor(null); constructor.setAccessible(true); SingletonHungry singletonHungry = constructor.newInstance();
System.out.println(instance); System.out.println(singletonHungry); } }
|
输出结果
com.meteor.design.SingletonHungry@2b05039f com.meteor.design.SingletonHungry@61e717c2
Process finished with exit code 0
|
很明显,正常获取的单例和反射获取的单例不一致
饿汉式不安全
懒汉式不安全实现,当要使用的时候调用方法获取,如果为空就创建,将静态对象返回
SingletonLazyUnsafe
package com.meteor.design;
public class SingletonLazyUnsafe { private SingletonLazyUnsafe() {
}
private static SingletonLazyUnsafe instance;
public static SingletonLazyUnsafe getInstance() { if (instance == null) { instance = new SingletonLazyUnsafe(); } return instance; } }
|
测试
测试懒汉式不安全实现,用多线程破坏单例
public class AppTest { @Test public void testSingletonLazyUnsafe(){ for(int i = 0; i < 5; i++){ new Thread(()->{ System.out.println(SingletonLazyUnsafe.getInstance()); }).start(); } } }
|
输出结果
com.meteor.design.SingletonLazyUnsafe@2d7143c2 com.meteor.design.SingletonLazyUnsafe@5fb48d1e com.meteor.design.SingletonLazyUnsafe@2d7143c2 com.meteor.design.SingletonLazyUnsafe@c087dd6 com.meteor.design.SingletonLazyUnsafe@2d7143c2
Process finished with exit code 0
|
不是每次都会出现问题,但肯定是要避免出问题的
懒汉式安全实现
使用 synchronized 关键字对方法加锁保证线程安全,但效率较低
SingletonLazySafe
package com.meteor.design;
public class SingletonLazySafe { private SingletonLazySafe() {
}
private static SingletonLazySafe instance;
public synchronized static SingletonLazySafe getInstance() { if (instance == null) { instance = new SingletonLazySafe(); } return instance; } }
|
内部类
使用静态内部类实现单例,静态内部类的特点是不依赖外部类的实例对象可以独立创建,调用getInstance()方法时才执行创建操作,实现了懒加载,而且JVM保证了执行实例化指令的线程安全
SingletonInnerClass
package com.meteor.design;
public class SingletonInnerClass { private SingletonInnerClass() {
}
public static InnerClass getInstance() { return InnerClass.INSTANCE; }
public static class InnerClass { private final static InnerClass INSTANCE = new InnerClass(); } }
|
双重校验锁
双重校验锁,先判断是否为空,再进行加锁,再次判断是否为空,如果为空则实例化
第二次判断是防止第一次判断为空但是加锁之后已经实例化好了,同时要配合volatile使用,volatile的作用是防止指令重排序和变量的值与主存的值时刻保持一致,也就是为了加速代码执行效率可能会进行指令重排序,在单线程环境下不会影响结果,但是多线程环境下可能会拿到一个不为null,但还未初始化的对象,禁止重排序后,就只能先初始化,后赋值给对象,同时对对象的修改对其它线程可见
SingletonDoubleCheckedLocking
package com.meteor.design;
public class SingletonDoubleCheckedLocking { private SingletonDoubleCheckedLocking() {
}
private static volatile SingletonDoubleCheckedLocking instance;
private static SingletonDoubleCheckedLocking getInstance() { if (instance == null) { synchronized (SingletonDoubleCheckedLocking.class) { if (instance == null) { instance = new SingletonDoubleCheckedLocking(); } } } return instance; } }
|
枚举
利用枚举实现单例,线程安全且无法通过反射破坏单例,因为反射调用的方法检测到是枚举类时会报错
SingletonEnum
package com.meteor.design;
public enum SingletonEnum { INSTANCE }
|
目录结构
src ├── main │ └── java │ └── com.meteor.design │ ├── dynamic_proxy │ │ ├── cglib │ │ │ ├── CGLIBProxyFactory.java │ │ │ └── MyMethodInterceptor.java │ │ │ │ │ └── jdk │ │ ├── JDKProxyFactory.java │ │ └── MyInvocationHandler.java │ │ │ ├── service │ │ ├── IUserService.java │ │ └── UserService.java │ │ │ └── static_proxy │ └── StaticProxy.java │ └── test └── java └── com.meteor.design └── AppTest.java
|