Java单例模式小记

单例模式是设计模式中最简单、也是使用最多的一种,但是有些细节的地方需要注意。
最近在补设计模式的知识,记录一下我比较喜欢的几种单例实现方式以便日后翻阅。本文的知识与代码借鉴于下面链接的博文。
原文地址:
http://cantellow.iteye.com/blog/838473
http://www.hollischuang.com/archives/205

第一种单例模式(不建议这样做)
/**
* 懒汉单例
*/
private static Singleton singleton;

private Singleton(){}

public static synchronized Singleton getInstance() {

    if (singleton == null) {
        singleton = new Singleton();
    }

    return singleton;
}

这种单例写法看起来能适应多线程,但实际上因为每个线程在执行的时候都需要等待上一线程释放锁,所以性能会较差。


第二种单例模式(不明确需要lazy loading时可以这样做)
public class Singleton {
    /**
    * 饿汉单例,使用classloder机制保证线程安全,但没有lazy loading
     */
    private static Singleton instance = new Singleton();

    private Singleton() {}

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

这种单例写法”急切”创建实例,而不用延迟实例化的做法,如果应用程序总是创建并使用单件实例,或者在创建和运行时方法的负担不太繁重,这种写法是可行的,而且因为类加载机制的关系,不用担心线程安全的问题(Java中所有类都是由JVM的类加载器装载进内存的,而类加载器在加载类的整个过程是线程安全的)
想知道为什么类加载机制能保证线程安全的朋友建议阅读一下 深度分析Java的ClassLoader机制(源码级别)

第三种单例模式(需要lazy loading时可以这样做)
public class Singleton {

    private Singleton(){}

    /**
    * 静态内部类,使用classloder机制保证线程安全,lazy loading
    */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

}

这种方式使用了静态内部类的方法实现单例模式,特点是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显式通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的,此时使用静态内部类会显得很合理。

第四种单例模式(以前我觉得合理的单例模式)
/**
* 解决多线程产生多个实例问题
* tips:双重检查加锁不适用于1.4及更早版本的java。
*/
public class Singleton {

    private volatile static Singleton instance;

    private Singleton(){}

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) { 
                if (instance == null) {
                    instance= new Singleton();
                }
            }
        }
        return instance;
    }

}

此处使用“双重检查加锁”,添加关键字volatile,在getInstance()中减少使用同步。
volatile关键词确保:当instance变量被初始化成singleton实例时,多个线程正确地处理instance变量。

注意

如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。

解决办法(实现readResolve()方法):

public class Singleton implements java.io.Serializable { 

   public static Singleton INSTANCE = new Singleton();     

   protected Singleton() {     

   }     

   private Object readResolve() {     
      return INSTANCE;
   }

}   

当然,还有更优雅的解决办法,比如使用enum实现单例模式。

总结

所以说,一般单例都是五种写法。懒汉,饿汉,双重校验锁,枚举和静态内部类。

你可能感兴趣的:(Java,设计模式,单例模式,设计模式)