素材巴巴 > 程序开发 >

单例模式详细学习

程序开发 2023-09-20 21:57:49

1.对单例模式的理解

核心: 构造方法私有化,保证在整个项目中该类的实例最多只有一个

分类:

image.png

饿汉式与懒汉式的区别:

饿汉式都是在类初始化就创建对象,该阶段的线程安全交由JVM来保证,我们不用考虑线程安全问题。而懒汉式需要我们人为思考如何解决线程安全问题。

2.写出5种单例模式的具体实现

普通饿汉式

/*** 饿汉式  只要类初始化该实例就已被创建,提前创建*/
 public class Singleton1 implements Serializable {private Singleton1(){//防止通过反射技术来破环单例if(INSTANCE != null){throw new RuntimeException("单例对象不能重复创建");}System.out.println("private Singleton1()");}private static final Singleton1 INSTANCE = new Singleton1();public static  Singleton1 getInstance(){return INSTANCE;}//防止反序列化破环单例public Object readResolve(){return INSTANCE;}
 }
 

枚举饿汉式

/*** 枚举饿汉式,只要类初始化该枚举对象就已被创建,提前创建*/
 public enum Singleton2{INSTANCE;//枚举类的构造方法默认修饰符就是privateSingleton2(){System.out.println("private Singleton2");}public static Singleton2 getInstance(){return INSTANCE;}}
 

普通懒汉式

/*** 懒汉式,要注意线程安全问题破环单例*/
 public class Singleton3 implements Serializable {private Singleton3(){System.out.println("private Singleton3()");}private static Singleton3 INSTANCE = null;//加入synchronized防止多线程安全问题,虽能解决,但严重影响性能public static synchronized Singleton3 getInstance(){if(INSTANCE == null){INSTANCE = new Singleton3();}return INSTANCE;}//防止反序列化破环单例public Object readResolve(){return INSTANCE;}
 }
 

DCL懒汉式

/*** 懒汉式 DCL(Double Checked Lock),双检锁解决线程安全*/
 public class Singleton4 implements Serializable {private Singleton4(){System.out.println("private Singleton4()");}private static volatile Singleton4 INSTANCE = null;//可见性,有序性,加入volatile防止指令重排//加锁之前先判断,只有当INSTANCE还未创建阶段才会加锁,一旦INSTANCE创建完毕,则直接返回,提高了性能public static  Singleton4 getInstance(){if(INSTANCE == null){synchronized (Singleton4.class){if(INSTANCE == null){INSTANCE = new Singleton4();}}}return INSTANCE;}//防止反序列化破环单例public Object readResolve(){return INSTANCE;}
 }
 
为什么用 volatile?

这是因为new关键字创建对象不是原子操作,会经历以下三步:

  1. 在堆内存开辟内存空间
  2. 调用构造方法,初始化对象
  3. 引用变量指向堆内存空间

操作系统有权对指令的执行顺序进行优化,改变在逻辑上没有关联的指令的执行顺序。这是1 2 3三个步骤就有可能变为 1 3 2,那么就有可能出线程1刚执行完1 3 步,还未给对象初始化,线程2执行到了INSTACE == nullif判断,结果未false,直接返回了还未初始化的对象。而一旦加了volatile关键字,就会禁止对INSTANCE赋值过程时的指令重排列。

静态内部类懒汉式

/*** 懒汉式 - 静态内部类实现(比较推荐的懒汉式实现方式)* 由于静态成员变量的赋值会放在静态代码块执行,而静态代码块会由JVM来保证线程安全问题,它里面的代码只会被执行一次*/
 public class Singleton5 {private Singleton5() {System.out.println("private Singleton5");}private static class Holder {static Singleton5 INSTANCE = new Singleton5();}public static Singleton5 getInstance() {return Holder.INSTANCE;}
 }
 

3. 如何破环单例?

3.1. 反射(对枚举饿汉式无效)

public static void reflection(Class clazz) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {Constructor constructor = clazz.getDeclaredConstructor();constructor.setAccessible(true);  //修改访问权限System.out.println("反射获得实例:"+constructor.newInstance());
 }
 

3.2 反序列化(看该类是否有readResolve()方法)

public static void serializable(Object instance) throws IOException, ClassNotFoundException {ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(instance);ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));//该新对象的创建不走构造方法//然而要注意当执行readObject方法时,会去原类里寻找readResolve方法,如果找到走该方法逻辑,如果该方法不存在则直接创建新对象System.out.println("反序列化创建实例:"+ ois.readObject());}
 

3.3 Unsafe类(无预防措施)

 public static void unsafe(Class clazz) throws InstantiationException {//这里需要借助Spring提供的UnsafeUtils拿到Unsafe对象,该方法同样不走构造方法,无法避免Object o = UnsafeUtils.getUnsafe().allocateInstance(clazz);System.out.println("Unsafe创建实例:"+ o);}
 

4. 哪里用到了单例模式?

1. JDK

2. Windows


标签:

上一篇: 【 C 11 】lambda表达式 下一篇:
素材巴巴 Copyright © 2013-2021 http://www.sucaibaba.com/. Some Rights Reserved. 备案号:备案中。