AKSTIANYE

Aug 04, 2018

java设计模式之单例模式

单例模式

  1. 单例类只能有一个实例
  2. 单例类必须自己创建自己的唯一实例。
  3. 单例类必须给所有其他对象提供这一实例。

常见的几种写法

  • 懒汉式(线程不安全)

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton {
private static Singleton instance;

private Singleton() {
}

public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
  • 懒汉式(线程安全)

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton {
private static Singleton instance;

private Singleton() {
}

public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
> 懒加载,但效率低
  • 饿汉式

1
2
3
4
5
6
7
8
9
10
public class Singleton {
private static Singleton instance = new Singleton();

private Singleton() {
}

public static Singleton getInstance() {
return instance;
}
}
> 不具备懒加载
  • 静态内部类

1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton {
private Singleton() {
}

public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}

private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
}
> 懒加载
  • 枚举

1
2
3
4
5
6
public enum Singleton {
INSTANCE;

public void whateverMethod() {
}
}
> 推荐
> 能避免多线程同步问题
> 能防止反序列化重新创建新的对象
  • 双重校验锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Singleton {
private volatile static Singleton singleton;

private Singleton() {
}

public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}

一些扩展

如何破坏单例
  • 反射
    虽然我们私有了构造方法,但是反射仍然能够通过AccessibleObject.setAccessible访问到私有方法,如:
    1
    2
    3
    4
    Class clazz = Singleton.class;
    Constructor cons = clazz.getDeclaredConstructor(null);
    cons.setAccessible(true);
    Singleton singleton = (Singleton) cons.newInstance(null);
  • 反序列化
    反序列化通过ObjectInputputStream来实现,下面来看代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public final Object readObject()
    throws IOException, ClassNotFoundException
    {
    ...
    ...
    int outerHandle = passHandle;
    try {
    Object obj = readObject0(false);
    handles.markDependency(outerHandle, passHandle);
    ...
    ...
    }
    进入readObject0方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    private Object readObject0(boolean unshared) throws IOException {
    ...
    ...
    try {
    switch (tc) {
    case TC_NULL:
    return readNull();
    case TC_REFERENCE:
    return readHandle(unshared);
    ...
    ...
    case TC_OBJECT:
    return checkResolve(readOrdinaryObject(unshared));
    case TC_EXCEPTION:
    ...
    }
    进入readOrdinaryObject方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    private Object readOrdinaryObject(boolean unshared)
    throws IOException
    {
    ...
    Object obj;
    try {
    obj = desc.isInstantiable() ? desc.newInstance() : null;
    } catch (Exception ex) {
    throw (IOException) new InvalidClassException(
    desc.forClass().getName(),"unable to create instance").initCause(ex);
    }
    ...
    }

    由此看出,反序列化也是通过反射的方式破坏单例的

防止单例被破坏
  • 使用单元素枚举
  • 单例类中定义readResolve方法

参考

深入理解单例模式(上)
深入理解单例模式(下)

OLDER > < NEWER