面试: 单例模式

目录

一、饿汉单例(实现Serializable)

1、破坏单例的三种情况

(1)反射破坏单例

(2)反序列化破坏单例

(3)Unsafe破坏单例

2、饿汉单例(利用枚举实现)

二、懒汉单例(DCL实现)

1、多线程时会发生的错误

2、懒汉单例(内部类实现)

三、在哪些地方用到了单例模式


一、饿汉单例(实现Serializable)

在类执行初始化操作时,单例就会被创建,JVM会保证线程安全。

java 复制代码
public class Singleton implements Serializable{

	private Singleton() {
		System.out.println("111");
	}

	private static final Singleton INS = new Singleton();//先创建好对象
	
	public static Singleton getInstance() {//在需要使用时直接调用
		return INS;
	}
	
}

1、破坏单例的三种情况

(1)反射破坏单例
java 复制代码
reflection(Singleton.class);

反射后创建了两个对象,使得单例被破坏。要想预防,在单例接口中加上以下代码

java 复制代码
	private Singleton() {
		if(INS!=null) {
			throw new RuntimeException("单例不可被创建");
		}
		System.out.println("111");
	}

直接抛出异常。

(2)反序列化破坏单例
java 复制代码
serializable(Singleton.getInstance());
  • 将单例对象转换为字节流,又将字节流读取为对象,就实现了单例破坏;
  • 因为重新被读取的对象并不是原来的对象,而是新建的一个对象。
java 复制代码
	private 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()));
		system.out.println("反序列化创建实例:" + ois.readObject());
	}

想要预防,在单例代码中实现这个方法

java 复制代码
public Object readResolve(){
    return INS;
}

这样就会利用这个方法的返回值作为结果返回,就不会用字节数组反序列化生成的对象了,这样就保证了对象的单例。

(3)Unsafe破坏单例
java 复制代码
unsafe(Singleton.class);

利用实例的类型来创建一个实例,不会走构造方法来创建。

java 复制代码
private static void unsafe(Class<?> clazz) throws InstantiationException {
    Object o = Unsafeutils.getUnsafe().allocateInstance(clazz);
    system.out.println( "Unsafe 创建实例:" + o);
}

2、饿汉单例(利用枚举实现)

java 复制代码
public enum Singleton{
    INS;
}
  • 这种方法实现可以避免被,反射和反序列化破坏单例;
  • 在反射时,java会做相应的检查,以阻止调用枚举类的构造。
  • 在反序列化时,java会直接返回枚举类实现的对象。
  • 挡不住unsafe。

二、懒汉单例(DCL实现)

java 复制代码
public class singleton implements serializable {
    private singleton() { system.out.println("private Singleton()");}
    private static volatile singleton INSTANCE = null;
    public static singleton getInstance() {
        if (INSTANCE == null) {
            synchronized (singleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new singleton();
                }
            }
        }
    return INSTANCE;
}
  • 双端检索可以避免对象被重复创建;
  • 如果没有第二次检查null,就可能发生,一个等待锁的线程,进入创建对象的程序;
  • 添加volatile关键字可以避免指令重排序,因为在JVM中,构建对象的指令有一定的因果关系,如果不使用volatile方法的话,就会产生构建失败的情况。

1、多线程时会发生的错误

  • 在JVM执行INSTANCE=对象后,另一个线程直接返回了INSTANCE对象,但是这个对象的成员变量是没有被完全赋值的;
  • 在加上volatile后,指令后会加上一个内存屏障,这样就阻止了内存重新排序。

2、懒汉单例(内部类实现)

既能保证线程安全,又可以消灭懒汉在第一次使用时才创建对象的特性。

三、在哪些地方用到了单例模式

  • Runtime类
  • System类
  • Collection类
  • Comparator接口
  • Comparators类
相关推荐
时差95314 分钟前
【面试题】Hive 查询:如何查找用户连续三天登录的记录
大数据·数据库·hive·sql·面试·database
让学习成为一种生活方式16 分钟前
R包下载太慢安装中止的解决策略-R语言003
java·数据库·r语言
晨曦_子画21 分钟前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
南宫生44 分钟前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法
Heavydrink1 小时前
HTTP动词与状态码
java
ktkiko111 小时前
Java中的远程方法调用——RPC详解
java·开发语言·rpc
计算机-秋大田1 小时前
基于Spring Boot的船舶监造系统的设计与实现,LW+源码+讲解
java·论文阅读·spring boot·后端·vue
神里大人1 小时前
idea、pycharm等软件的文件名红色怎么变绿色
java·pycharm·intellij-idea
小冉在学习2 小时前
day53 图论章节刷题Part05(并查集理论基础、寻找存在的路径)
java·算法·图论
CXDNW2 小时前
【网络面试篇】HTTP(2)(笔记)——http、https、http1.1、http2.0
网络·笔记·http·面试·https·http2.0