线程安全的单例

饿汉模式:线程安全,没有性能问题

1
2
3
4
5
6
7
8
public class Planet{
private Planet(){};
//月球 初始化的时候直接创建了月球。
private static Planet moon = new Planet();
public static Planet getMoon(){
return moon;
}
}

采用双重检查加锁并使用volatile关键字的懒汉模式,保证对象在使用的时候才创建,而且线程安全,性能最优

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Planet{
private Planet(){};
//地球
private volatile static Planet earth;
public static Planet getEarth(){
if(earth == null){
synchronized(Planet.class){
if(earth == null){
earth = new Planet();//注释1
}
}
}
return earth;
}
}
  • volatile关键字的作用是确保在注释1创建对象的时候,多个线程能够正确的处理earth变量,杜绝指令重排。
  • earth = new Planet();创建对象时会被分为三个步骤:

    1
    2
    3
    memory = allocate(); //1:分配对象的内存空间
    ctorInstance(memory); //2:调用构造函数,初始化对象
    instance = memory; //3:返回地址给引用
  • 如果没有使用volatile关键字,那么就无序了,从而会导致这三个步骤顺序可能变成1、3、2的情况,在还没有调用构造函数的情况下,就把地址返回给引用了,这时在2还未执行的情况下当前线程挂起了,其他线程来调用的时候发现该对象已经不是null了,直接拿来使用,这就会出现意料之外的问题。

  • 因为对象是定义为静态了,所以创建之后是不会被垃圾回收的。