在面试的时候应该经常会问道的一种题型,使用某种语言实现单例模式,这里就记录一下常见的几种方法。
单例模式:实现一个只能生成一个实例的类。
方法一
1
2
3
4
5
6
7
8
9
public Singleton{
private Singleton instance=null;
private Singleton(){}
public static Singleton getInstance{
if(instance==null)
instance = new Singleton();
return instance;
}
}
缺点:只能在单线程的环境工作,如果两个线程同时运行到if()判断,则会创建两个实例
方法二
1
2
3
4
5
6
7
8
9
public class singleton {
private static singleton instance=null;
private singleton(){}
public synchronized static singleton getInstance(){
if(instance==null)
instance=new singleton();
return instance;
}
}
加同步锁的方法,可以在多线程的情况下实现单例模式,但是执行的效率并不高,每一次执行getInstance都会尝试加锁,而加锁也是一个比较耗时的操作。
方法三
1
2
3
4
5
6
7
8
9
10
11
12
13
public class singleton1{
private volatile static singleton1 instance =null;
private singleton1(){};
public static singleton1 getInstacne(){
if(instance==null){
synchronized(singleton1.class){
if(instance==null)
instance=new singleton1();
}
}
return instance;
}
} //加入volatile防止指令重排序的问题
目前认为比较好的方法,只有当尝试创建实例的时候,才加锁
方法四
1
2
3
4
5
6
7
8
9
public class singleton2{
private singleton2(){}
public static singleton2 getInstance(){
return singleholder.single;
}
private static class singleholder{
private static sinleton2 single=new singleton2();
}
}
1
2
3
4
5
6
7
public class singleton2{
private static singleton2 single=new singleton2();
private singleton2(){}
public static singleton2 getInstance(){
return single;
}
}
方法四中给了两种方法,但是第一种更好(只是相差比较细微的差别),他们都没有加锁操作效率更高,内部类只加载一次。他们都是在类加载的时候,创建singleton2实例。
第一种只有明显的调用 singleholder.single
才会创建实例,因为singleholder类加载了。
第二种一般也是调用singleton2.getInstance
才会实现singleton2类加载从而创建singleton2的实例,但是如果该类有其他的静态方法调用(调用该类的静态方法,你并不想实现实例),也会创建singleton2实例。
方法五
1
2
3
4
public enum singleton3{
INSTANCE;
public void method(){...}
}
使用枚举实现单例,出现在《effective Java》中(后悔买了,看不懂),说是能够绝对的防止多次实例化,即使是在面对复杂的序列化或则反射攻击的时候。书上说单元素的枚举类型是实现单例模式的最佳方法。
我个人比较推崇方法三和四。