设计模式之单例
郭旭升 Lv6

singleton

思想

确保一个类只有一个实例,并提供该实例的全局访问点

使用一个私有构造函数、一个私有静态变量、一个公有的静态函数来实现。
这样只能通过这个公有的静态函数来返回唯一的私有静态变量。

实现方式

  1. 懒汉式-线程不安全

私有静态变量uniqueInstance在使用的时候,被实例化

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

private Singleton() {
}

public static Singleton getUniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}

这种实现方式在多线程的环境下会存在多个线程同时进入if语句,创建多个实例。

  1. 饿汉式-线程安全

在类加载时,直接赋值创建实例,这样保证不会重复创建实例。

1
private static Singleton uniqueInstance = new Singleton();
  1. 懒汉式-同步机制

通过给静态函数加锁,一个线程创建时,其他线程只能等待,保证了线程安全。但是锁粒度比较大,开销大。

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

private Singleton() {
}

public static synchronized Singleton getUniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
  1. 懒汉式-双重检测
    将锁的粒度减小,只需要给实例化的部分加锁,
    首先判断是否已经生成实例,如果没有生成实例的时候才给实例化部分加锁。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class Singleton {
    private volatile static Singleton uniqueInstance;

    private Singleton() {
    }

    public static Singleton getUniqueInstance() {
    if (uniqueInstance == null) {
    synchronized (Singleton.class) {
    if (uniqueInstance == null) {
    uniqueInstance = new Singleton();
    }
    }
    }
    return uniqueInstance;
    }
    }
    如果是这样,在没有实例化的时候,两个线程进入了if语句,所以加了同步控制,但是只是先后的顺序去实例化,会产生多实例。
    所以需要双重检测,同步的情况下,第一个线程实例化之后,第二个语句进入同步块时,判断if的条件就不成立,也就控制了单实例。
    1
    2
    3
    4
    5
    if (uniqueInstance == null) {
    synchronized (Singleton.class) {
    uniqueInstance = new Singleton();
    }
    }

volatile 关键字修饰uniqueInstance, 因为uniqueInstance = new Singleton();语句分三步执行:
(1) 开辟存储空间
(2) 初始化对象
(3) 让uniqueInstance实例指向空间。
JVM具有指令重排的特性,可能执行顺序会编程1-3-2,多线程的情况下可能产生没有初始化的实例。volatile可以禁止指令重排,在多线程下正常运行。

  1. 静态内部类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class Singleton {


    private Singleton() {
    }

    private static class SingletonHolder {
    private static Singleton INSTANCE = new Singleton();
    }

    public static Singleton getUniqueInstance() {
    return SingletonHolder.INSTANCE;
    }
    }
  2. 枚举实现
    实现简单,在序列化和反射攻击时,可以防止多实例。
    1
    2
    3
    public enum Singleton {
    uniqueInstance;
    }
 Comments