单例模式是我们编程中最常用的设计模式,小记练习一下:
1、饿汉法:
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
这是最简单的方法,在类引用的时候就会实例化。不能延迟加载,某种意义上会增加系统的负载。
2、懒汉法:
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
在饿汉法的基础上增加了延迟加载,只有调用getInstance的方法才会实例化对象,在单线程中没有问题,但在多线程中有可能导致多次实例化。
3、多线程安全型:
public class Singleton {
private static volatile Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
return instance;
}
}
在instance对象加上volatile关键字,保证其对所有的线程可见性,并禁止其对指令重排序优化。在判断null和new的部分进行加锁。但是每次调用的时候,都要通过synchronize锁机制排队等待,会有效率方面的问题。
4、多线程安全性兼顾效率(双重检查锁):
public class Singleton {
private static volatile Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
在synchronized关键字外层再加一个判断null,只有为null后才会进入方法内,可以减少绝大部分的锁操作。
==注意:volatile关键字在JDK 1.5之前无法完全保证指令优化重排序,所以在JDK1.5之前双重校验是有问题的==
5、静态内部类
public class Singleton {
private Singleton(){}
private static class Holder{
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return Holder.instance;
}
}
将创建的过程放在静态内部类中,静态内部类只有被调用的时候才会加载,所以符合延迟加载。静态内部类只会被加载一次,所以是线程安全的。
以上所有方式,在序列化的时候都需要额外的工作(Serializable、transient、readResolve()),否则在反序列化的时候都会创建一个新的实例。
6、枚举方式:
public enum Singleton {
INSTACE;
private String name;
public String getName() {
return name;
}
}
使用枚举方式实现的单例模式,自带线程安全和防止反射强行调用构造器,还提供了自动序列化的机制,防止反序列化的时候创建新的对象。