构造单例3要素
- 私有构造方法 。
- 私有静态引用指向自己实例 。
- 以自己实例为返回值的公有静态方法。
单例的实现
- 懒汉式,线程不安全。
public class Singleton {
private static Singleton instance;
private Singleton() {
//构造方法私有化
}
public static Singleton getInstance() {
if (instance == null) {
return new Singleton();
}
return instance;
}
2.饿汉式,线程安全
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
//构造方法私有化
}
public static Singleton getInstance() {
return instance;
}
}
3.双重检测
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
//构造方法私有化
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
return new Singleton();
}
}
}
return instance;
}
}
为什么Singleton属性要用volatitle修饰?
对象创建过程:
- 创建对象实例,分配内存。
- 调用构造函数,初始化。
- 将对象引用赋值给变量。
多线程情况下,2,3可能发生指令重排序
t1 | t2 | |
---|---|---|
t1 | 分配内存 | |
t2 | 变量赋值 | |
t3 | 判断对象是否为空 | |
t4 | 对象不为空,返回未初始化的队象 | |
t5 | 初始化对象 |
线程2可以访问到还未初始化的对象。
4.静态内部类
public class Singleton {
private Singleton() {
//构造方法私有化
}
private static class SingletonHolder {
private static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
5.登记式单例
实际上维护的是一组单例类的实例,将这些实例存储到一个Map(登记簿)中,对于已经登记过的单例,则从工厂直接返回,对于没有登记的,则先登记,而后返回。
- 使用map实现注册表;
- 使用protect修饰构造方法;