单例模式的多种玩法

Posted 怒放de每一天

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式的多种玩法相关的知识,希望对你有一定的参考价值。

饿汉单例模式

简单饿汉单例模式

 
   
   
 
  1. public class HungrySingleton {

  2. private static final HungrySingleton hungrySington = new HungrySingleton();


  3. private HungrySingleton() {

  4. }

  5. public static HungrySingleton getInstance(){

  6. return hungrySington;

  7. }

  8. }

静态代码块实现饿汉模式

 
   
   
 
  1. public class HungryStaticSingleton {

  2. private static final HungryStaticSingleton hungrySington;


  3. static {

  4. hungrySington = new HungryStaticSingleton();

  5. }


  6. private HungryStaticSingleton() {

  7. }


  8. public static HungryStaticSingleton getInstance() {

  9. return hungrySington;

  10. }

  11. }

懒汉单例模式

简单懒汉单例模式(线程不安全)

 
   
   
 
  1. public class LazySimplySington {

  2. private static LazySimplySington lazy;


  3. private LazySimplySington() {

  4. }


  5. //jdk1.8之后对synchronized性能优化不少

  6. //不可避免还是存在一定的性能问题

  7. public synchronized static LazySimplySington getInstance() {

  8. if (lazy == null) {

  9. lazy = new LazySimplySington();

  10. }

  11. return lazy;

  12. }

  13. }

双重检查实现单例模式(线程安全)

注意指令重排序问题,需要单独加voliate关键字

 
   
   
 
  1. public class LazyDoubleCheckSington {


  2. private volatile static LazyDoubleCheckSington lazy = null;


  3. private LazyDoubleCheckSington() {

  4. }


  5. //jdk1.8之后对synchronized性能优化不少

  6. //不可避免还是存在一定的性能问题

  7. public static LazyDoubleCheckSington getInstance() {

  8. if (lazy == null) {

  9. synchronized (LazyDoubleCheckSington.class) {

  10. if (lazy == null) {

  11. lazy = new LazyDoubleCheckSington();

  12. //指令重排序的问题:也就是 第二步和第三步会颠倒,

  13. // 解决方式 变量上加voliate,让线程可见


  14. //CPU执行时候会转换成JVM指令执行

  15. //1.分配内存给这个对象

  16. //2.初始化对象

  17. //4.用户初次始化

  18. }

  19. }

  20. }

  21. return lazy;

  22. }

  23. }

内部类实现单例模式(线程安全)

 
   
   
 
  1. public class LazyInnerClassSington {


  2. //虽然构造方法有了,但是逃不过反射的法眼

  3. private LazyInnerClassSington() {

  4. }

  5. //懒汉式单例

  6. //LazyHolder里面的逻辑需要等到外部方法调用时才执行

  7. //巧妙利用了内部类的特性

  8. //JVM底层执行逻辑,完美的避免了线程安全问题

  9. public static final LazyInnerClassSington getInstance(){

  10. return LazyHolder.lazy;

  11. }

  12. private static class LazyHolder{

  13. private static final LazyInnerClassSington lazy = new LazyInnerClassSington();

  14. }

  15. }

暴力破解单例

反射暴力破解

 
   
   
 
  1. public class LazyInnerClassSingtonTest {

  2. public static void main(String[] args) {

  3. try {

  4. //反射,破坏了单例

  5. Class<?> clazz = LazyInnerClassSington.class;

  6. Constructor<?> c = clazz.getDeclaredConstructor(null);

  7. c.setAccessible(true);

  8. Object o = c.newInstance();

  9. Object o2 = c.newInstance();

  10. System.out.println(o == o2);

  11. }catch (Exception e){

  12. e.printStackTrace();

  13. }

  14. }

  15. }

单例模式的多种玩法

解决方式:

在构造函数内部添加判断

 
   
   
 
  1. //虽然构造方法有了,但是逃不过反射的法眼

  2. private LazyInnerClassSington() {

  3. if (LazyHolder.lazy != null){

  4. throw new RuntimeException("不允许构建多个实例");

  5. }

  6. }

测试:单例模式的多种玩法完整代码:

 
   
   
 
  1. public class LazyInnerClassSington {


  2. //虽然构造方法有了,但是逃不过反射的法眼

  3. private LazyInnerClassSington() {

  4. if (LazyHolder.lazy != null){

  5. throw new RuntimeException("不允许构建多个实例");

  6. }

  7. }

  8. //懒汉式单例

  9. //LazyHolder里面的逻辑需要等到外部方法调用时才执行

  10. //巧妙利用了内部类的特性

  11. //JVM底层执行逻辑,完美的避免了线程安全问题

  12. public static final LazyInnerClassSington getInstance(){

  13. return LazyHolder.lazy;

  14. }

  15. private static class LazyHolder{

  16. private static final LazyInnerClassSington lazy = new LazyInnerClassSington();

  17. }

  18. }

序列化和反序列化暴力破解

来个饿汉单例

 
   
   
 
  1. //反序列化时导致单例破坏

  2. public class SeriableSingleton implements Serializable {


  3. //序列化就是说把内存中的状态通过转换成字节码的形式

  4. //从而转换一个IO流,写入到其他地方(可以是磁盘、网络IO)

  5. //内存中状态给永久保存下来了


  6. //反序列化

  7. //讲已经持久化的字节码内容,转换为IO流

  8. //通过IO流的读取,进而将读取的内容转换为Java对象

  9. //在转换过程中会重新创建对象new


  10. public final static SeriableSingleton INSTANCE = new SeriableSingleton();

  11. private SeriableSingleton(){}


  12. public static SeriableSingleton getInstance(){

  13. return INSTANCE;

  14. }

破解

 
   
   
 
  1. public class SeriableSingletonTest {

  2. public static void main(String[] args) {


  3. SeriableSingleton s1 = null;

  4. SeriableSingleton s2 = SeriableSingleton.getInstance();


  5. FileOutputStream fos = null;

  6. try {

  7. fos = new FileOutputStream("SeriableSingleton.obj");

  8. ObjectOutputStream oos = new ObjectOutputStream(fos);

  9. oos.writeObject(s2);

  10. oos.flush();

  11. oos.close();



  12. FileInputStream fis = new FileInputStream("SeriableSingleton.obj");

  13. ObjectInputStream ois = new ObjectInputStream(fis);

  14. s1 = (SeriableSingleton)ois.readObject();

  15. ois.close();


  16. System.out.println(s1);

  17. System.out.println(s2);

  18. System.out.println(s1 == s2);


  19. } catch (Exception e) {

  20. e.printStackTrace();

  21. }

  22. }

  23. }

解决方式resolve方法

加个方法:

 
   
   
 
  1. //序列化解决单例问题的方式

  2. //重写readResolve方法,只不过是覆盖了反序列化出来的对象

  3. //还是创建了两次,发生在JVM层面,相对来说比较安全

  4. //之前反序列化出来的对象会被GC回收

  5. private Object readResolve(){

  6. return INSTANCE;

  7. }

完整代码:

 
   
   
 
  1. public class SeriableSingletonTest {

  2. public static void main(String[] args) {


  3. SeriableSingleton s1 = null;

  4. SeriableSingleton s2 = SeriableSingleton.getInstance();


  5. FileOutputStream fos = null;

  6. try {

  7. fos = new FileOutputStream("SeriableSingleton.obj");

  8. ObjectOutputStream oos = new ObjectOutputStream(fos);

  9. oos.writeObject(s2);

  10. oos.flush();

  11. oos.close();



  12. FileInputStream fis = new FileInputStream("SeriableSingleton.obj");

  13. ObjectInputStream ois = new ObjectInputStream(fis);

  14. s1 = (SeriableSingleton)ois.readObject();

  15. ois.close();


  16. System.out.println(s1);

  17. System.out.println(s2);

  18. System.out.println(s1 == s2);


  19. } catch (Exception e) {

  20. e.printStackTrace();

  21. }

  22. }

  23. }

注册类单例

枚举实现单例(推荐)

可以同时实现防止反射和序列化反序列化暴力破解

 
   
   
 
  1. public enum EnumSingleton {

  2. INSTANCE;


  3. private String name;


  4. public String getData() {

  5. return name;

  6. }


  7. public void setData(String data) {

  8. this.name = data;

  9. }


  10. public static EnumSingleton getInstance(){

  11. return INSTANCE;

  12. }

  13. }

使用jad工具查看枚举类的反编译代码

单例模式的多种玩法单例模式的多种玩法反编译代码:

 
   
   
 
  1. // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.

  2. // Jad home page: http://www.kpdus.com/jad.html

  3. // Decompiler options: packimports(3)

  4. // Source File Name: EnumSingleton.java


  5. package com.example.sington.register;



  6. public final class EnumSingleton extends Enum

  7. {


  8. public static EnumSingleton[] values()

  9. {

  10. return (EnumSingleton[])$VALUES.clone();

  11. }


  12. public static EnumSingleton valueOf(String name)

  13. {

  14. return (EnumSingleton)Enum.valueOf(com/example/sington/register/EnumSingleton, name);

  15. }


  16. private EnumSingleton(String s, int i)

  17. {

  18. super(s, i);

  19. }


  20. public Object getData()

  21. {

  22. return data;

  23. }


  24. public void setData(Object data)

  25. {

  26. this.data = data;

  27. }


  28. public static EnumSingleton getInstance()

  29. {

  30. return INSTANCE;

  31. }


  32. public static final EnumSingleton INSTANCE;

  33. private Object data;

  34. private static final EnumSingleton $VALUES[];


  35. static

  36. {

  37. INSTANCE = new EnumSingleton("INSTANCE", 0);

  38. $VALUES = (new EnumSingleton[] {

  39. INSTANCE

  40. });

  41. }

  42. }

容器实现单例

 
   
   
 
  1. public class ContainerSingleton {


  2. private ContainerSingleton() {

  3. }


  4. private static Map<String, Object> ioc = new ConcurrentHashMap<>();


  5. public static Object getBean(String className) {

  6. synchronized (ioc){

  7. if (!ioc.containsKey(className)) {

  8. Object obj = null;

  9. try {

  10. obj = Class.forName(className).getInterfaces();

  11. ioc.put(className, obj);

  12. } catch (Exception e) {

  13. e.printStackTrace();

  14. }

  15. }

  16. return ioc.get(className);

  17. }

  18. }

  19. }

 
   
   
 
  1. public class Pojo {

  2. }

 
   
   
 
  1. public class ContainerSingletonTest {

  2. public static void main(String[] args) {



  3. try {

  4. long start = System.currentTimeMillis();

  5. ConcurrentExecutor.execute(new ConcurrentExecutor.RunHandler() {

  6. public void handler() {

  7. Object obj = ContainerSingleton.getBean("com.example.Pojo");

  8. System.out.println(System.currentTimeMillis() + ": " + obj);

  9. }

  10. }, 10,6);

  11. long end = System.currentTimeMillis();

  12. System.out.println("总耗时:" + (end - start) + " ms.");

  13. }catch (Exception e){

  14. e.printStackTrace();

  15. }


  16. }

  17. }

单例模式的多种玩法

线程间实现单例ThreadLocal

 
   
   
 
  1. public class ThreadLocalSingleton {



  2. private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance =

  3. new ThreadLocal<ThreadLocalSingleton>() {

  4. @Override

  5. protected ThreadLocalSingleton initialValue() {

  6. return new ThreadLocalSingleton();

  7. }

  8. };


  9. private ThreadLocalSingleton() {

  10. }


  11. public static ThreadLocalSingleton getInstance() {

  12. return threadLocalInstance.get();

  13. }

  14. }

 
   
   
 
  1. public class ExectorThread implements Runnable {

  2. @Override

  3. public void run() {

  4. // LazySimplySington instance = LazySimplySington.getInstance();

  5. // LazyDoubleCheckSington instance = LazyDoubleCheckSington.getInstance();

  6. ThreadLocalSingleton instance = ThreadLocalSingleton.getInstance();

  7. System.out.println(Thread.currentThread().getName()+" ---"+instance);

  8. ThreadLocalSingleton instance2 = ThreadLocalSingleton.getInstance();

  9. System.out.println(Thread.currentThread().getName()+" ---"+instance2);

  10. ThreadLocalSingleton instance3 = ThreadLocalSingleton.getInstance();

  11. System.out.println(Thread.currentThread().getName()+" ---"+instance3);

  12. ThreadLocalSingleton instance4 = ThreadLocalSingleton.getInstance();

  13. System.out.println(Thread.currentThread().getName()+" ---"+instance4);

  14. ThreadLocalSingleton instance5 = ThreadLocalSingleton.getInstance();

  15. System.out.println(Thread.currentThread().getName()+" ---"+instance5);


  16. System.out.println(Thread.currentThread().getName()+" ---"+instance);

  17. }

  18. }

 
   
   
 
  1. public class ThreadLocalSingletonTest {

  2. public static void main(String[] args) {


  3. System.out.println(ThreadLocalSingleton.getInstance());

  4. System.out.println(ThreadLocalSingleton.getInstance());

  5. System.out.println(ThreadLocalSingleton.getInstance());

  6. System.out.println(ThreadLocalSingleton.getInstance());

  7. System.out.println(ThreadLocalSingleton.getInstance());


  8. Thread t1 = new Thread(new ExectorThread());

  9. Thread t2 = new Thread(new ExectorThread());

  10. t1.start();

  11. t2.start();

  12. System.out.println("End");


  13. }

  14. }

单例模式的多种玩法可以看到线程间是单例的。

单例模式的本质

本质:控制实例数目。(研磨设计模式)

笔记和代码地址

代码:https://github.com/hufanglei/pattern-learn/tree/master/src/main/java/com/example/sington

笔记https://blog.csdn.net/baidu_21349635/article/details/106067581


微信订阅号:搜索:怒放de每一天

个人微信公号,搜索:i69032866

不定时推送相关文章,期待和大家一起成长!!



以上是关于单例模式的多种玩法的主要内容,如果未能解决你的问题,请参考以下文章

实现单例设计模式的多种方式

单例模式的多种实现

Java单例模式 多种实现方式

java学习-单例模式的多种实现方式

聊聊“单例模式”的多种实现方式。

单例模式的多种写法