声明:写这个内容主要是便于自己记忆和理解,这边有更好的文章http://www.tekbroaden.com/singleton-java.html
第一种:饿汉模式--即类加载时就进行实例化,无论是否会用到该实例,都要预先加载,而我们一般希望延迟加载,减小负载。
public class Singleton {
//恶汉模式,即类加载时就创建实例
//1.私有化构造器
private Singleton(){
}
//2.声明并实例化,注意是私有,并且为静态的类成员
private static Singleton instance = new Singleton();
//3.使用公有的静态函数返回实例
public static Singleton getInstance(){
return instance;
}
}
第二种:懒汉模式--一开始只声明,不进行实例化。当需要使用实例时才创建。
public class Singleton2 {
//1.私有化构造器
private Singleton2(){}
//2.声明,注意是私有,并且为静态的类成员
private static Singleton2 instance;
//3.使用公有的静态函数返回实例
public static Singleton2 getInstance(){
if(instance == null){
instance = new Singleton2();
}
return instance;
}
}
这种写法线程不安全,当有多个线程访问时,可能会创建多个实例,因此可以加一个锁。
public static synchronized Singleton2 getInstance(){
if(instance == null){
instance = new Singleton2();
}
return instance;
}
但是这个方法每次都要进行同步,即使实例存在的时候,所以效率不高,由此引出下一种方法。
第三种:双重检查锁,首先加了双重检查,如果实例存在的话,就不会进入同步的代码了,提高了效率。第二个要注意的地方是实例声明的时候加了一个volatile关键字,这个关键字有两层语义,1是可见性,2是禁止指令重排序优化。因为JVM会自己进行指令排序的优化,当多线程访问的时候,指令的执行顺序与源代码不一样,就会出问题。volatile就是解决这个问题的,但这要在jdk1.5之后才有作用。
public class Singleton2 {
//1.私有化构造器
private Singleton2(){}
//2.声明,注意是私有,并且为静态的类成员
private static volatile Singleton2 instance;
//3.使用公有的静态函数返回实例
public static Singleton2 getInstance(){
if(instance == null){
synchronized (Singleton2.class){
if(instance == null) instance = new Singleton2();
}
}
return instance;
}
}
第四种:静态内部类,因为是静态内部类,所以Sington3类加载时不会创建实例,而且因为是静态内部类所有只会加载一次,所以也是线程安全的。
public class Singleton3 {
//1.私有化构造器
private Singleton3(){
}
//静态的内部类来创建实例,这样只有调用实例方法的时候才会创建
private static class Holder{
private static Singleton3 instance = new Singleton3();
}
//3.使用公有的静态函数返回实例
public static Singleton3 getInstance(){
return Holder.instance;
}
}
上述这四种方法都有缺点:序列化和反序列化需要额外的工作来实现,要注意反射强行调用私有构造方法的问题。
这篇文章中的说明http://www.tekbroaden.com/singleton-java.html
都需要额外的工作(Serializable、transient、readResolve())来实现序列化,否则每次反序列化一个序列化的对象实例时都会创建一个新的实例。
可能会有人使用反射强行调用我们的私有构造器(如果要避免这种情况,可以修改构造器,让它在创建第二个实例的时候抛异常)。
第五种:枚举类
public enum Singleton4 {
INSTANCE;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
还有一种接口形式实现的枚举类,感觉很厉害!!!
public interface MySingleton {
void doSomething();
}
public enum Singleton5 implements MySingleton {
INSTANCE {
@Override
public void doSomething(){
}
};
public static MySingleton getInstance(){
return INSTANCE;
}
}