饿汉法
顾名思义,饿汉法就是在第一次引用该类的时候就创建对象实例,而不管实际是否需要创建。代码如下:
1
2
3
4
5
6
7
|
public class Singleton {
private static Singleton = new Singleton();
private Singleton() {}
public static getSignleton(){
return singleton;
}
}
|
这样做的好处是编写简单,但是无法做到延迟创建对象。但是我们很多时候都希望对象可以尽可能地延迟加载,从而减小负载,所以就需要下面的懒汉法:
懒汉法:单线程写法
1
2
3
4
5
6
7
8
|
public class Singleton {
private static Singleton singleton = null ;
private Singleton(){}
public static Singleton getSingleton() {
if (singleton == null ) singleton = new Singleton();
return singleton;
}
}
|
懒汉法:考虑线程安全的写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public class Singleton {
private static volatile Singleton singleton = null ;
private Singleton(){}
public static Singleton getSingleton(){
synchronized (Singleton. class ){
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 static volatile Singleton singleton = null ;
private Singleton(){}
public static Singleton getSingleton(){
if (singleton == null ){
synchronized (Singleton. class ){
if (singleton == null ){
singleton = new Singleton();
}
}
}
return singleton;
}
}
|
这种写法被称为“双重检查锁”,在单例中new的情况非常少,绝大多数都是可以并行的读操作。因此在加锁前多进行一次null检查就可以减少绝大多数的加锁操作,执行效率提高的目的也就达到了。
懒汉法:静态内部类法
那么,有没有一种延时加载,并且能保证线程安全的简单写法呢?我们可以把Singleton实例放到一个静态内部类中,这样就避免了静态实例在Singleton类加载的时候就创建对象,并且由于静态内部类只会被加载一次,所以这种写法也是线程安全的:
1
2
3
4
5
6
7
8
9
10
11
|
public class Singleton {
private static class Holder {
private static Singleton singleton = new Singleton();
}
private Singleton(){}
public static Singleton getSingleton(){
return Holder.singleton;
}
}
|
但是,上面提到的所有实现方式都有两个共同的缺点:
- 都需要额外的工作(Serializable、transient、readResolve())来实现序列化,否则每次反序列化一个序列化的对象实例时都会创建一个新的实例。
- 可能会有人使用反射强行调用我们的私有构造器(如果要避免这种情况,可以修改构造器,让它在创建第二个实例的时候抛异常)。
枚举写法
当然,还有一种更加优雅的方法来实现单例模式,那就是枚举写法:
1
2
3
4
5
6
7
8
9
10
|
public enum Singleton {
INSTANCE;
private String name;
public String getName(){
return name;
}
public void setName(String name){
this .name = name;
}
}
|
使用枚举除了线程安全和防止反射强行调用构造器之外,还提供了自动序列化机制,防止反序列化的时候创建新的对象。