什么是单例模式
单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例。即一个类只有一个对象实例
其中单例模式的具体实现如下:
饿汉形式——当类被加载的时候便进行初始化单例对象
直接在加载后初始化
public class SingleDemo{
private static SingleDemo demo = new SingleDemo();
private SingleDemo(){}
public static SingleDemo getDemo(){
return demo;
}
}
以及通过静态代码块实现
public class SingleDemo{
private static SingleDemo demo = null;
static{
demo = new SingleDemo();
}
private SingleDemo(){}
public static SingleDemo getDemo(){
return demo;
}
}
虽然看上去饿汉模式不会有线程安全的问题,但是却依然有一个隐晦的漏洞,那就是当有多个类加载器对该对象进行装载的时候,便会初始化多个demo对象。例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。
懒汉形式——当需要使用单例对象时进行初始化操作(多版本)
####版本1:
public class SingleDemo{
private static SingleDemo demo = null;
private SingleDemo(){}
public static SingleDemo getDemo(){
if(demo == null){
demo = new SingleDemo();
}
return demo;
}
}
这是最简单的版本,但是有个致命的问题就是,它不是一个线程安全的例子,也就是说在多线程环境下,并不总能保证单例。
版本2:
public class SingleDemo{
private static SingleDemo demo = null;
private SingleDemo(){}
public synchronized static SingleDemo getDemo(){
if(demo == null){
demo = new SingleDemo();
}
return demo;
}
}
通过对方法上锁,实现的线程安全的获取单例的操作,但是很遗憾,因为这个方法在绝大多数情况下是不需要同步的,加锁严重降低了程序执行的效率,所以不提倡使用。
版本3:
public class SingleDemo{
private static SingleDemo demo = null;
private SingleDemo(){}
public static SingleDemo getDemo(){
if(demo == null){
synchronized(SingleDemo.class) {
demo = new SingleDemo();
}
}
return demo;
}
}
对版本2进行优化后,可以明显提高程序的效率,但是很遗憾,这段代码并不能保证线程安全。
版本4:
public class SingleDemo{
private static volatile SingleDemo demo = null;
private SingleDemo(){}
public static SingleDemo getDemo(){
if(demo == null){
synchronized(SingleDemo.class) {
if(demo == null) {
demo = new SingleDemo();
}
}
}
return demo;
}
}
这是上述版本的最终形态,俗称双检锁模式,通过双重校验,达到了线程安全的目的,同时避免了代码频繁上锁解锁的过程。
版本5:
public class SingleDemo{
private SingleDemo(){}
private static class Singleton{
private static final SingleDemo DEMO = new SingleDemo();
}
public static SingleDemo getDemo(){
return Singleton.DEMO;
}
}
这样至少看起来代码简洁不少,不仅达到了懒加载的效果,还实现了线程安全。
版本6:
public enum SingleDemo{
DEMO;
public static SingleDemo getDemo(){
return DEMO;
}
}
最后这种是通过枚举实现的单例,但是现实用到的不多,该模式为Effective Java作者Josh Bloch 所推崇,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象(如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例)。