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

本文正在参加「Java主题月 – Java Debug笔记活动」,详情查看 活动链接

没看书之前,一直以为单例模式只有两种,看了书之后,发现居然有八种!
冲冲冲?

单例模式八个例子

实现单例模式的八种模式:饿汉式,懒汉式,双重检查锁模式,静态内部类模式,序列化模式,注册式之枚举,注册式之容器,线程实现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. 反射破坏单例模式

反射破坏单例模式

我是 4ye 我们下期再见啦 ヾ( ̄▽ ̄)ByeBye

欢迎关注,交个朋友呀!! ( •̀ ω •́ )y

嘿嘿 喜欢就支持下啦 ?

让我们开始这一场意外的相遇吧!~

欢迎留言!谢谢支持!ヾ(≧▽≦*)o

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享