单例模式八个例子 | Java Debug 笔记

简介: 实现单例模式的八种模式:饿汉式,懒汉式,双重检查锁模式,静态内部类模式,序列化模式,注册式之枚举,注册式之容器,线程实现ThreadLocal参考大神Tom的《Spring 5核心原理与30个类手写实战-谭勇德》单例模式 Singleton Pattern确保一个类在任何场景下只有一个实例,并提供一个全局访问点使用场景J2EE 标准中的 ServletContext 、 Spring 框架应用中的ApplicationContext 、数据库的连接池 也都是单例形式饿汉式在类加载的时候就立即初始化,并且创建单例对象,属于线程安全SpringIOC容器Applic

实现单例模式的八种模式:饿汉式,懒汉式,双重检查锁模式,静态内部类模式,序列化模式,注册式之枚举,注册式之容器,线程实现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();
    }
  }
}
复制代码


  1. 懒汉式


网络异常,图片无法展示
|


  1. 反射破坏单例模式


网络异常,图片无法展示
|




目录
相关文章
|
6天前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
|
7天前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑】设计模式——原型模式
对比原型模式和传统方式的实现思路、代码方案、优缺点,阐述原型模式的使用场景,以及深拷贝、浅拷贝等相关概念,并扩展原型模式在Spring源码中的应用。
【Java笔记+踩坑】设计模式——原型模式
|
7天前
|
Java 开发者 数据格式
【Java笔记+踩坑】SpringBoot基础4——原理篇
bean的8种加载方式,自动配置原理、自定义starter开发、SpringBoot程序启动流程解析
【Java笔记+踩坑】SpringBoot基础4——原理篇
消息中间件 缓存 监控
29 0
|
7天前
|
运维 Java 关系型数据库
【Java笔记+踩坑】SpringBoot基础2——运维实用
SpringBoot程序的打包与运行、临时配置、多环境配置、日志
【Java笔记+踩坑】SpringBoot基础2——运维实用
|
7天前
|
Java 数据库连接 API
【Java笔记+踩坑】Spring Data JPA
从常用注解、实体类和各层编写方法入手,详细介绍JPA框架在增删改查等方面的基本用法,以及填充用户名日期、分页查询等高级用法。
【Java笔记+踩坑】Spring Data JPA
|
7天前
|
SQL Java 数据库连接
【Java笔记+踩坑】MyBatisPlus基础
MyBatisPlus简介、标准数据层开发CRUD、业务层继承IService、ServiceImpl、条件查询、LambdaQueryWrapper、id生成策略、逻辑删除、乐观锁@Version、代码生成器、ActiveRecord
【Java笔记+踩坑】MyBatisPlus基础
|
7天前
|
前端开发 Java 数据库连接
【Java笔记+踩坑】SpringBoot——基础
springboot三种配置文件及其优先级、多环境配置、springboot整合junit,mybatis、ssmp综合图书案例
【Java笔记+踩坑】SpringBoot——基础
|
7天前
|
Java 数据库连接 Maven
【Java笔记+踩坑】Maven高级
分模块开发、依赖传递与冲突问题、 可选依赖和排除依赖、聚合和继承、属性、多环境配置与应用、私服安装和使用
【Java笔记+踩坑】Maven高级
|
7天前
|
前端开发 Java 数据库连接
【Java笔记+踩坑】SSM整合
统一结果封装、统一异常处理、整合图书案例、拦截器
【Java笔记+踩坑】SSM整合