优秀的编程知识分享平台

网站首页 > 技术文章 正文

「设计模式专题」Singleton(23种设计模式 平常使用)

nanyue 2024-09-06 20:23:51 技术文章 8 ℃

单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。

饿汉式单例模式

优点

  • 线程安全
  • 在类加载的同事已经创建好一个静态对象,调用时反应速度快

缺点

  • 资源效率不高,可能 getInstance() 永远不会执行到
  • 执行该类的其他静态方法或者加载了该类 (class.forName),那么这个实例仍然能初始化
 public class HungrySingleton {
 
 private final static HungrySingleton instance = new HungrySingleton();
 
 private HungrySingleton() {
 }
 
 public static HungrySingleton getInstance() {
 return instance;
 }
 
 public static void main(String[] args) {
 
 /** 线程安全 */
 int count = 1000;
 CountDownLatch countDownLatch = new CountDownLatch(count);
 for (int i = 0; i < count; i++) {
 new Thread(() -> {
 HungrySingleton instance = HungrySingleton.getInstance();
 System.out.println(System.currentTimeMillis() + ":" + instance);
 countDownLatch.countDown();
 }).start();
 }
 
 try {
 countDownLatch.await();
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 
 /** 通过反射 那么这个实例仍然能初始化 */
 // try {
 // Class<?> clazz = Class.forName("hungry.HungrySingleton");
 // Object instance1 = clazz.newInstance();
 // Object instance2 = clazz.newInstance();
 //
 // System.out.println(instance1);
 // System.out.println(instance2);
 // } catch (ClassNotFoundException e) {
 // e.printStackTrace();
 // } catch (IllegalAccessException e) {
 // e.printStackTrace();
 // } catch (InstantiationException e) {
 // e.printStackTrace();
 // }
 }
 }

懒汉式单例模式

Lazy_v1_SimpleSingleton

优点

  • 避免了饿汉式的那种在没有用到的情况下创建实例,资源利用率高

缺点

  • 线程不安全
  • 执行该类的其他静态方法或者加载了该类 (class.forName),那么这个实例仍然初始化
 public class Lazy_v1_SimpleSingleton {
 private static Lazy_v1_SimpleSingleton instance;
 
 private Lazy_v1_SimpleSingleton() {
 }
 
 public static Lazy_v1_SimpleSingleton getInstance() {
 if (instance == null) {
 instance = new Lazy_v1_SimpleSingleton();
 }
 return instance;
 }
 
 }

Lazy_v2_SynchronizedSingleton

优点

  • 避免了饿汉式的那种在没有用到的情况下创建实例,资源利用率高
  • 线程安全

缺点

  • 第一次加载时不够快,多线程使用不必要的同步开销大
  • 执行该类的其他静态方法或者加载了该类(class.forName),那么这个实例仍然初始化
 public class Lazy_v2_SynchronizedSingleton {
 private static Lazy_v2_SynchronizedSingleton instance;
 
 private Lazy_v2_SynchronizedSingleton() {
 }
 
 public synchronized static Lazy_v2_SynchronizedSingleton getInstance() {
 if (instance == null) {
 instance = new Lazy_v2_SynchronizedSingleton();
 }
 return instance;
 }
 
 public static void main(String[] args) {
 try {
 Class<?> clazz = Class.forName("lazy.Lazy_v2_SynchronizedSingleton");
 Object instance1 = clazz.newInstance();
 Object instance2 = clazz.newInstance();
 System.out.println(instance1);
 System.out.println(instance2);
 } catch (ClassNotFoundException e) {
 e.printStackTrace();
 } catch (IllegalAccessException e) {
 e.printStackTrace();
 } catch (InstantiationException e) {
 e.printStackTrace();
 }
 }
 }

Lazy_v3_DoubleCheckSingleton

优点

  • 避免了饿汉式的那种在没有用到的情况下创建实例,资源利用率高
  • 线程安全

缺点

  • 执行该类的其他静态方法或者加载了该类(class.forName),那么这个实例仍然初始化
  • 由于java内存模型一些原因偶尔失败
  • 非原子操作
  • 给 instance 分配内存
  • 调用 instance 的构造函数来初始化成员变量,形成实例
  • 将 instance 对象指向分配的内存空间(执行完这步 singleton才是非 null 了) --> 123 or 132
  • 结论:如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错;由于有一个『instance已经不为null但是仍没有完成初始化』的中间状态,而这个时候,如果有其他线程刚好运行到第一层 if (instance == null) 这里,这里读取到的 instance 已经不为 null 了,所以就直接把这个中间状态的instance拿去用了,就会产生问题。
  • 解决办法
  • Lazy_v4_VolatileDoubleCheckSingleton 不让指令重排序 -lazy.Lazy_v5_InnerClassSingleton 可以指令重排序,但是不让外部看见
 public class Lazy_v3_DoubleCheckSingleton {
 private static Lazy_v3_DoubleCheckSingleton instance;
 
 private Lazy_v3_DoubleCheckSingleton() {
 }
 
 public static Lazy_v3_DoubleCheckSingleton getInstance() {
 if (instance == null) {
 synchronized (Lazy_v3_DoubleCheckSingleton.class) {
 if (instance == null) {
 instance = new Lazy_v3_DoubleCheckSingleton();
 }
 }
 }
 return instance;
 }
 
 public static void main(String[] args) {
 long count = 1000000;
 synchronizedSingleton(count);
 noSynchronizedSingleton(count);
 }
 
 private static void synchronizedSingleton(long count) {
 long start = System.currentTimeMillis();
 for (int i = 0; i < count; i++) {
 Lazy_v3_DoubleCheckSingleton synchronizedSingleton = Lazy_v3_DoubleCheckSingleton.getInstance();
 }
 long end = System.currentTimeMillis();
 System.out.println("synchronizedSingleton 耗时:" + (end - start));
 }
 
 private static void noSynchronizedSingleton(long count) {
 long start = System.currentTimeMillis();
 for (int i = 0; i < count; i++) {
 HungrySingleton hungrySingleton = HungrySingleton.getInstance();
 }
 long end = System.currentTimeMillis();
 System.out.println("noSynchronizedSingleton 耗时:" + (end - start));
 }
 }

Lazy_v4_VolatileDoubleCheckSingleton

优点

  • 避免了饿汉式的那种在没有用到的情况下创建实例,资源利用率高
  • 线程安全

缺点

  • 执行该类的其他静态方法或者加载了该类(class.forName),那么这个实例仍然初始化
  • 禁止指令重排,导致性能下降
 public class Lazy_v4_VolatileDoubleCheckSingleton {
 /** volatile关键字的一个作用是禁止指令重排,把instance声明为volatile之后,对它的写操作就会有一个内存屏障 */
 private static volatile Lazy_v4_VolatileDoubleCheckSingleton instance;
 
 private Lazy_v4_VolatileDoubleCheckSingleton() {
 }
 
 public static Lazy_v4_VolatileDoubleCheckSingleton getInstance() {
 if (instance == null) {
 synchronized (Lazy_v4_VolatileDoubleCheckSingleton.class) {
 if (instance == null) {
 instance = new Lazy_v4_VolatileDoubleCheckSingleton();
 }
 }
 }
 return instance;
 }
 }

Lazy_v5_InnerClassSingleton

优点

  • 避免了饿汉式的那种在没有用到的情况下创建实例,资源利用率高
  • 线程安全

缺点

  • 执行该类的其他静态方法或者加载了该类(class.forName),那么这个实例仍然初始化
  • 禁止指令重排,导致性能下降
 public class Lazy_v5_InnerClassSingleton {
 
 /**
 * static 是为了使单例的空间共享
 * final 保证这个方法不会被重写,重载
 */
 public static final Lazy_v5_InnerClassSingleton getInstance() {
 return SingletonHolder.instance;
 }
 
 /**
 * 这种写法非常巧妙:
 * 对于内部类 SingletonHolder,它是一个饿汉式的单例实现,在SingletonHolder初始化的时候会
 * 由ClassLoader来保证同步,使 instance 是一个真·单例。
 * 同时,由于SingletonHolder是一个内部类,只在外部类的Singleton的getInstance()中被使用,
 * 所以它被加载的时机也就是在getInstance()方法第一次被调用的时候。
 * <p>
 * 从内部看是一个饿汉式的单例,但是从外部看来,又的确是懒汉式的实现。
 */
 private static class SingletonHolder {
 private static final Lazy_v5_InnerClassSingleton instance = new Lazy_v5_InnerClassSingleton();
 }
 
 /** 防止 反射 攻击! */
 private static boolean initialized = false;
 
 private Lazy_v5_InnerClassSingleton() {
 synchronized (Lazy_v5_InnerClassSingleton.class){
 if(initialized == false){
 initialized = !initialized;
 }
 else{
 throw new RuntimeException("单例已被侵犯");
 }
 }
 }
 
 public static void main(String[] args) {
 try {
 
 Class<?> clazz = Lazy_v5_InnerClassSingleton.class;
 Constructor c = clazz.getDeclaredConstructor(null);
 c.setAccessible(true);
 Object o = c.newInstance();
 System.out.println(o);
 Object o1 = c.newInstance();
 System.out.println(o1);
 Object o2 = c.newInstance();
 System.out.println(o2);
 
 } catch (Exception e) {
 e.printStackTrace();
 }
 }
 }

防止序列反序列话单例模式

 public class SerializableSingleton implements Serializable {
 
 private static final long serialVersionUID = -1994001829338263901L;
 
 private SerializableSingleton() {
 }
 
 public final static SerializableSingleton INSTANCE = new SerializableSingleton();
 
 public static SerializableSingleton getInstance(){
 return INSTANCE;
 }
 
 /**
 * @method_name: readResolve
 * @describe: 防止 序列化和反序列化后 破坏单例模式规则 启用 readResolve() 方法
 **/
 public Object readResolve() throws ObjectStreamException{
 return INSTANCE;
 }
 
 public static void main(String[] args) {
 SerializableSingleton s1;
 SerializableSingleton s2 = SerializableSingleton.getInstance();
 FileInputStream fis;
 ObjectInputStream ois;
 
 FileOutputStream fos = null;
 try {
 fos = new FileOutputStream("serialize.obj");
 ObjectOutputStream oos = new ObjectOutputStream(fos);
 oos.writeObject(s2);
 oos.flush();
 oos.close();
 
 fis = new FileInputStream("serialize.obj");
 ois = new ObjectInputStream(fis);
 s1 = (SerializableSingleton) ois.readObject();
 ois.close();
 
 System.out.println(s1);
 System.out.println(s2);
 System.out.println(s1 == s2);
 
 } catch (Exception e) {
 e.printStackTrace();
 }
 }
 
 }

注册式单例模式

RegisterSingleton

优点

  • 登记式单例模式可以被挤成(饿汉和懒汉模式构造方法都是稀有的,因而不能被继承的)
  • 登记式单例实际上维护的是一组单例类的实例,将这些实例存储到一个Map(登记簿)中,对于已经登记过的单例,则从工厂直接返回,对于没有登记的,则先登记,而后返回
 public class RegisterSingletonChildA extends RegisterSingleton {
 public static RegisterSingletonChildA getInstance() {
 return (RegisterSingletonChildA) RegisterSingletonChildA
 .getInstance("org.crayzer.demo.singleton.register.RegisterSingletonChildA");
 }
 
 //随便写一个测试的方法
 public String about() {
 return "---->我是RegisterSingleton的一个子类RegisterSingletonChildA";
 }
 }
 
 public class RegisterSingletonChildB extends RegisterSingleton {
 public static RegisterSingletonChildB getInstance() {
 return (RegisterSingletonChildB) RegisterSingletonChildB
 .getInstance("org.crayzer.demo.singleton.register.RegisterSingletonChildB");
 }
 
 //随便写一个测试的方法
 public String about() {
 return "---->我是RegisterSingleton的一个子类RegisterSingletonChildB";
 }
 }
 
 public class RegisterSingleton {
 private static Map<String, RegisterSingleton> registerSingletonMap = new ConcurrentHashMap<>();
 
 public static Map<String, RegisterSingleton> getRegisterSingletonMap() {
 return registerSingletonMap;
 }
 
 protected RegisterSingleton() {
 System.out.println("RegisterSingleton 的构造函数被调用,创建实例中...");
 }
 
 public static synchronized RegisterSingleton getInstance(String name) {
 if (name == null) {
 name = RegisterSingleton.class.getName();
 System.out.println("name不存在,name = " + name);
 }
 
 if (registerSingletonMap.get(name) == null) {
 try {
 System.out.println("-->name对应的值不存在,开始创建");
 registerSingletonMap.put(name, (RegisterSingleton) Class.forName(name).newInstance());
 } catch (IllegalAccessException e) {
 e.printStackTrace();
 } catch (InstantiationException e) {
 e.printStackTrace();
 } catch (ClassNotFoundException e) {
 e.printStackTrace();
 }
 }else {
 System.out.println("-->name对应的值存在");
 }
 System.out.println("-->返回name对应的值");
 return registerSingletonMap.get(name);
 }
 
 /**
 * 测试 多线程环境
 * @param args
 */
 public static void main(String[] args) {
 // testMultithreaded();
 // System.out.println("=============华丽的分割线=============");
 testExtends();
 
 }
 
 private static void testExtends() {
 System.out.println("-----------------登记式单例模式----------------");
 System.out.println("第一次取得实例(登记式)");
 RegisterSingleton s1 = RegisterSingleton.getInstance(null);
 System.out.println(s1);
 System.out.println("第二次取得实例(登记式)");
 RegisterSingletonChildA s3 = RegisterSingletonChildA.getInstance();
 System.out.println(s3);
 System.out.println(s3.about());
 System.out.println("第三次取得实例(登记式)");
 RegisterSingletonChildB s4 = RegisterSingletonChildB.getInstance();
 System.out.println(s4);
 System.out.println(s4.about());
 System.out.println("第四次取得实例(登记式)");
 RegisterSingletonChildB s5 = RegisterSingletonChildB.getInstance();
 System.out.println(s5);
 System.out.println(s5.about());
 System.out.println("第五次取得实例(非正常直接new子类的构造方法)");
 RegisterSingletonChildB s6 = new RegisterSingletonChildB();
 System.out.println(s6);
 System.out.println(s6.about());
 System.out.println("输出父类中Map保存的所有单例,可以看出,直接new出来的实例并没有存在Map中");
 System.out.println(s1.getRegisterSingletonMap());
 System.out.println();
 }
 
 public static void testMultithreaded() {
 int count = 100;
 CountDownLatch latch = new CountDownLatch(count);
 for (int i = 0; i < count; i++) {
 
 new Thread(() -> {
 RegisterSingleton singleton = RegisterSingleton.getInstance("org.crayzer.demo.singleton.register.RegisterSingleton");
 System.out.println(System.currentTimeMillis() + ":" + singleton);
 latch.countDown();
 }).start();
 }
 
 try {
 latch.await();
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 
 RegisterSingleton singleton = new RegisterSingleton();
 System.out.println(singleton.getRegisterSingletonMap());
 RegisterSingletonChildA singleton1 = new RegisterSingletonChildA();
 System.out.println(singleton1.getRegisterSingletonMap());
 RegisterSingletonChildB singleton2 = new RegisterSingletonChildB();
 System.out.println(singleton2.getRegisterSingletonMap());
 }
 
 }

Enum

 public enum EnumSingleton {
 RED(){
 private int r = 255;
 private int g = 0;
 private int b = 0;
 
 },BLACK(){
 private int r = 0;
 private int g = 0;
 private int b = 0;
 },WHITE(){
 private int r = 255;
 private int g = 255;
 private int b = 255;
 };
 
 public void func1() {
 
 }
 
 public static void main(String[] args) {
 System.out.println(EnumSingleton.RED);
 }
 }


最近发表
标签列表