查看原文
其他

一文带你看遍单例模式的八个例子,面试再也不怕被问了

Java4ye Java4ye 2022-09-04

单例模式八个例子

实现单例模式的八种模式:饿汉式,懒汉式,双重检查锁模式,静态内部类模式,序列化模式,注册式之枚举,注册式之容器,线程实现ThreadLocal


  • 使用场景

    • 饿汉式

    • 懒汉式

    • 双重检查锁模式

    • 静态内部类模式

    • 序列化模式

    • 注册式单例模式

    • 线程单例实现 Thread Local

    • 测试


参考大神Tom的《Spring 5核心原理与30个类手写实战-谭勇德》

单例模式 Singleton Pattern

确保一个类在任何场景下只有一个实例,并提供一个全局访问点

使用场景

J2EE 标准中的 ServletContext Serv etContextConfig 等、 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;

  }



}


静态内部类模式

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("不允许创建多个实例");

    }

  }





}


序列化模式

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

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(10201, 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();

    }

  }





}
  1. 懒汉式
懒汉式 线程不安全 测试
  1. 反射破坏单例模式
反射破坏单例模式


创作不易, 点个赞,关注下  交个朋友可好! xiexie~ 😝


谢谢可爱又帅气的大佬们的观看!祝您 天天开心!😄  


感谢您的关注!您的每个关注,都是博主生发的动力 😝

         

点个“在看”表示朕

摸鱼小技巧之IDEA调试篇一


SpringCloud Gateway 的使用(一)


Springboot 居然可以设置动态的 banner !!(悄悄滴~)



您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存