单例模式你不一定懂

常见单例模式

首先我们来看看常见的单例模式都哪些实现方式:

饿汉式:

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

饿汉式中singleton在java虚拟机加载字节码的时候就已经被分配了空间所以就不存在线程安全问题。

懒汉式(延迟加载)

实现方式一
1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton{
private static Singleton singleton=null;
private Singleton(){}
public synchronized static Singleton getInstance(){
if(singleton==null){
singleton=new Singleton();
}
return singleton;
}
}

方式一虽然不存在线程安全问题,但是同步是在整个方法上进行的,效率低下,其实只需要第一次创建实例的时候需要同步,创建成功后就不需要再同步了。

实现方式二
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Singleton{
private volatile static Singleton singleton=null;
private Singleton(){}
public static Singleton getInstance(){
if(singleton==null){
synchronized(Singleton.class){
if(singleton==null){
singleton=new Singleton();
}
}
}
return singleton;
}
}

要注意的一点是一定要volatile,当一个线程对singleton进行分配空间,初始化,调用构造函数的时候是在自己的线程工作栈中,其他线程是看不到这个过程的,Java虚拟机一切都是按引用的值复制的.向主存储区同步其实就是把线程工作存储区的这个已经构造好的对象有压缩堆地址值COPY给主存储区的那个变量。不加volatile,有可能发生另一个线程会获取到还没有实例化的singleton引用。
参考文献:
Double-checked Locking (DCL) and how to fix it

实现方式三

利用内部内延迟加载的特性。内部类也是个单独的Class文件,只有用到的时候才会动态加载

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

实现方式三

静态方法块

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