(一)单例模式详解 (四)

2014-11-24 10:41:12 · 作者: · 浏览: 1
ton {

public static Singleton getInstance(){
return Singleton.singleton;
}

private static class Singleton{

protected static Singleton singleton = new Singleton();

}
}
首先来说一下,这种方式为何会避免了上面莫名的错误,众所周知,一个类的静态属性只会在第一次加载类时会被初始化,这是JVM级别的限制,所以我们无需担心初始化进行一半的时候,有人使用了这个实例,而且由于静态变量只初始化一次,所以singleton仍然是单例的。另外我们也不需要担心并发所产生的问题,因为JVM会帮我们保证初始化只进行一次。上面这种写法是我们使用内部类作为单例,这样不太符合我们的习惯。我们改为以下形式。


[java]
public class Singleton {

private Singleton(){}

public static Singleton getInstance(){
return SingletonInstance.instance;
}

private static class SingletonInstance{

static Singleton instance = new Singleton();

}
}

public class Singleton {

private Singleton(){}

public static Singleton getInstance(){
return SingletonInstance.instance;
}

private static class SingletonInstance{

static Singleton instance = new Singleton();

}
} 好了,进行到这里,单例模式算是已经完成了。最终的产物就是如上述的形式。上述形式保证了以下几点。

1.Singleton最多只有一个实例,在不考虑反射强行突破访问限制的情况下。

2.保证了并发访问的情况下,不会发生由于并发而产生多个实例。

3.保证了并发访问的情况下,不会由于初始化动作未完全完成而造成使用了尚未正确初始化的实例。

另外,我在这里再给出几种不太常用的单例的构造方式,给出之后我会简单的说下这种方式的优缺点。


[java] view plaincopyprint public class Singleton {

private static Singleton singleton = new Singleton();

private Singleton(){}

public static Singleton getInstance(){
return singleton;
}

}

public class Singleton {

private static Singleton singleton = new Singleton();

private Singleton(){}

public static Singleton getInstance(){
return singleton;
}

} 上述方式与我们最后一种给出的方式类似,只不过没有经过内部类处理,这种方式最主要的缺点就是一旦我访问了Singleton的任何其他的静态域,就会造成实例的初始化,而事实是可能我们从始至终就没有使用这个实例,造成内存的浪费。

还有一种方式我就不贴了,与双重锁定一模一样,只是给静态的实例属性加上关键字volatile,标识这个属性是不需要优化的。这样也不会出现实例化发生一半的情况,因为加入了volatile关键字,就等于禁止了JVM自动的指令重排序优化,并且强行保证线程中对变量所做的任何写入操作对其他线程都是即时可见的。这里没有篇幅去介绍volatile以及JVM中变量访问时所做的具体动作,总之volatile会强行将对该变量的所有读和取操作绑定成一个不可拆分的动作。如果读者有兴趣的话,可以自行去找一些资料看一下相关内容。

不过值得注意的是,volatile关键字是在JDK1.5之后才被给予了意义,所以这种方式要在JDK1.5之后才可以使用。

好了,以上基本上就是常见的所有单例模式的构造方式,如果下次再有面试让你去写一个单例模式,有时间的话就把上面所有的全部写给面试官并一一将优劣讲给他听吧,这样的话估计offer已经离你不远了。

下期预告,代理模式。