总所周知,单例模式有两种比较常见的写法,一种是饿汉式,一种是懒汉式。
懒汉式如下:
class Single1{
private static volatile Single1 instance;
public static Single1 getInstance(){
if (instance==null){
instance = new Single1();
}
return instance;
}
private Single1(){}
}
就是只有等你调用getInstance的时候,我才会new对象,(只有你需要的时候我才做这件事,所以很懒,叫懒汉式)。
饿汉式如下:
class Single2{
private static Single2 instance=new Single2();
private Single2(){}
public static Single2 getInstance(){
return instance;
}
}
也就是一早就new 对象,(早早就准备好,所以也交饿汉式)。
这两个好像是挺容易的,但是懒汉式牵扯到多线程的时候就容易出问题,如果我两个线程同时调用getInstance的时候,可能就会创建两个对象了。
我们实际试验一下:
结果就出问题了,返回了两个不一样的对象。(心里骂至于一个对象的空间都省吗?)
所以这个时候就要引用锁了,
class Single1{
private static Single1 instance;
public static Single1 getInstance(){
synchronized (Single1.class) {
if (instance==null){
instance = new Single1();
}
}
return instance;
}
private Single1(){}
}
锁一下就不会出现问题吧,但是每一次getInstance都要所一下,然后判空等等,这样是不是显得有点烦?
所以就可以先检查一遍,然后在空的话在锁上,锁上之后判断是不是空的,是空的就创建对象。这就叫 双检查锁机制
private static Single1 instance;
public static Single1 getInstance(){
if (instance==null){
synchronized (Single1.class){
if (instance==null) {
instance = new Single1();
}
}
}
return instance;
}
private Single1(){}
}
但是众所周知,反射是无所不能的,
然后你就会非常开心的发现,如果是反射的时候,每一次都会创建一个新的对象,而这个对象和getInstance拿到的对象也是不一样的。
有人就会问,难道没有安全的单例模式了吗?
有,
就是用枚举做:
enum Single3{
INSTANCE;
private Single3(){}
public static Single3 getInstance(){
return INSTANCE;
}
}
结果会报错,因为emun可以防止反序列化重新创建对象。
另外还有一种写法是静态内部类
代码如下:
class Single4{
private static class MySingleton{
private static final Single4 INSTANCE=new Single4();
}
private Single4(){}
public static final Single4 getInstance(){
return MySingleton.INSTANCE;
}
}
这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。