跳到主要内容

设计模式篇

单例模式

要求

  • 掌握五种单例模式的实现方式
  • 理解为何 DCL 实现时要使用 volatile 修饰静态变量
  • 了解 jdk 中用到单例的场景

饿汉式

package com.jielihaofeng.interview.pattern;

import java.io.Serializable;

/**
* @description 饿汉式单例
* @author Johnnie Wind
* @date 2022/3/31 16:41
*/
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 static void otherMethod() {
System.out.println("otherMethod()");
}

// 防止反序列化破坏单例
public Object readResolve() {
return INSTANCE;
}
}

构造方法抛出异常是防止反射破坏单例 readResolve() 是防止反序列化破坏单例

枚举饿汉式

package com.jielihaofeng.interview.pattern;

/**
* @description 枚举饿汉式单例(枚举饿汉式能天然防止反射、反序列化破坏单例)
* @author Johnnie Wind
* @date 2022/3/31 16:45
*/
public enum Singleton2 {

INSTANCE;

private Singleton2() {
System.out.println("private Singleton2()");
}

@Override
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

public static Singleton2 getInstance() {
return INSTANCE;
}

public static void otherMethod() {
System.out.println("otherMethod()");
}
}

枚举饿汉式能天然防止反射、反序列化破坏单例

懒汉式

package com.jielihaofeng.interview.pattern;

/**
* @description 懒汉式单例
* @author Johnnie Wind
* @date 2022/3/31 17:06
*/
public class Singleton3 {

private Singleton3() {
// 防止反射破坏单例
if (INSTANCE != null) {
throw new RuntimeException("单例对象不能重复创建");
}
System.out.println("private Singleton3()");
}

private static Singleton3 INSTANCE = null;

// 这里有多线程并发问题,所以加了synchronized锁。如果INSTANCE创建好了,就不需要同步了,但后续线程调用还是串行化。
public static synchronized Singleton3 getInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton3();
}
return INSTANCE;
}

public static void otherMethod() {
System.out.println("otherMethod()");
}

// 防止反序列化破坏单例
public Object readResolve() {
return INSTANCE;
}

}

其实只有首次创建单例对象时才需要同步,但该代码实际上每次调用都会同步 因此有了下面的双检锁改进

双检锁懒汉式

package com.jielihaofeng.interview.pattern;

/**
* @description 懒汉式单例 DCL(Double Check Lock)
* @author Johnnie Wind
* @date 2022/3/31 17:06
*/
public class Singleton4 {

private Singleton4() {
// 防止反射破坏单例
if (INSTANCE != null) {
throw new RuntimeException("单例对象不能重复创建");
}
System.out.println("private Singleton4()");
}

// 这里必须加 volatile,解决可见性和有序性,防止指令重排序,
private static volatile Singleton4 INSTANCE = null;

// 双重检查锁改进
public static Singleton4 getInstance() {
if (INSTANCE == null) {
synchronized (Singleton4.class) {
if (INSTANCE == null) {
// 这里可能发生指令重排序:
// 1.创建对象,分配空间
// 2.调用构造,初始化
// 3.给静态变量赋值
// 如果第3和2互换了,就会导致另一个线程在最外层if判断时拿到一个初始化为完全的INSTANCE对象
INSTANCE = new Singleton4();
}
}
}
return INSTANCE;
}

public static void otherMethod() {
System.out.println("otherMethod()");
}

// 防止反序列化破坏单例
public Object readResolve() {
return INSTANCE;
}

}

为何必须加 volatile: INSTANCE = new Singleton4() 不是原子的,分成 3 步:创建对象、调用构造、给静态变量赋 值,其中后两步可能被指令重排序优化,变成先赋值、再调用构造 如果线程 1 先执行了赋值,线程 2 执行到第一个 INSTANCE == null 时发现 INSTANCE 已经不为 null,此时就会返回一个未完全构造的对象

静态内部类懒汉式

package com.jielihaofeng.interview.pattern;

/**
* @description 懒汉式单例 - 内部类
* @author Johnnie Wind
* @date 2022/3/31 17:18
*/
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;
}

public static void otherMethod() {
System.out.println("otherMethod");
}
}

避免了双检锁的缺点,比较推荐

JDK 中单例的体现

Runtime 体现了 饿汉式单例

public class Runtime {
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
private Runtime() {}
...
}

Collections 中的 EmptyNavigableSet 内部类懒汉式单例

{
...
private static class EmptyNavigableSet<E> extends UnmodifiableNavigableSet<E>
implements Serializable {
private static final long serialVersionUID = -6291252904449939134L;

public EmptyNavigableSet() {
super(new TreeSet<E>());
}

private Object readResolve() { return EMPTY_NAVIGABLE_SET; }
}

@SuppressWarnings("rawtypes")
private static final NavigableSet<?> EMPTY_NAVIGABLE_SET =
new EmptyNavigableSet<>();
...
}

ReverseComparator.REVERSE_ORDER 内部类懒汉式单例

{
...
public static <T> Comparator<T> reverseOrder() {
return (Comparator<T>) ReverseComparator.REVERSE_ORDER;
}

/**
* @serial include
*/
private static class ReverseComparator
implements Comparator<Comparable<Object>>, Serializable {

private static final long serialVersionUID = 7207038068494060240L;

static final ReverseComparator REVERSE_ORDER
= new ReverseComparator();

public int compare(Comparable<Object> c1, Comparable<Object> c2) {
return c2.compareTo(c1);
}

private Object readResolve() { return Collections.reverseOrder(); }

@Override
public Comparator<Comparable<Object>> reversed() {
return Comparator.naturalOrder();
}
}
...
}

Comparators.NaturalOrderComparator.INSTANCE 枚举饿汉式单例

{
...
enum NaturalOrderComparator implements Comparator<Comparable<Object>> {
INSTANCE;

@Override
public int compare(Comparable<Object> c1, Comparable<Object> c2) {
return c1.compareTo(c2);
}

@Override
public Comparator<Comparable<Object>> reversed() {
return Comparator.reverseOrder();
}
}
...
}

Console 体现了 双检锁懒汉式单例