单例的定义
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。即,在整个系统运行过程中,只允许产生一个实例(有且只有一个)。
饿汉式
在类加载的时候就立即初始化,并且创建单例对象
优点:没有加任何的锁、执行效率比较高,在用户体验上来说,比懒汉式更好;绝对线程安全,在线程还没出现以前就是实例化了,不可能存在访问安全问题
缺点:类加载的时候就初始化,不管你用还是不用,我都占着空间
public class Hungry {
private Hungry(){}
private static final Hungry hungry = new Hungry();
public static Hungry getInstance(){
return hungry;
}
}
懒汉式
这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
public class LazySingleton(){
private LazySingleton(){}
private static LazySingleton lazy = null;
public static synchronized LazySingleton getInstance(){
if(lazy == null){
lazy = new LazySingleton();
}
return lazy;
}
}
枚举式
这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。不能通过 reflection attack 来调用私有构造方法。
public enum EnumSingleton{
INSTANCE;
public void whateverMethod(){
}
}
静态内部类
利用了 classloader 机制来保证初始化 instance 时只有一个线程,延迟加载。在外部类被调用的时候内部类才会被加载,内部类一定是要在方法调用之前初始化,巧妙地避免了线程安全问题.这种形式兼顾饿汉式的内存浪费,也兼顾synchronized性能问题,完美地屏蔽了这两个缺点.
public class Singleton{
private Singleton(){}
public static final Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
}
双检锁/双重校验锁(DCL,即 double-checked locking)
这种方式采用双锁机制,安全且在多线程情况下能保持高性能。
public class DoubleSingleton{
private volatile static DoubleSingleton instance;
private DoubleSingleton(){}
public static DoubleSingleton getInstance(){
if(null == instance){
synchronized(DoubleSingle.class){
if(null == instance){
instance = new DoubleSingleton();
}
}
}
return instance;
}
}
如何防止反序列化破坏单例
public class SerializableSingleton implements Serializable {
public final static Seriable INSTANCE = new Seriable();
private Seriable(){}
public static Seriable getInstance(){
return INSTANCE;
}
// 该方法保证反序列化时返回的是同一个实例
private Object readResolve(){
return INSTANCE;
}
}
防止利用反射,获取私有构造函数二次创建实例
// 在构造函数内判断单例是否已存在,存在则抛出异常,防止反序列化破坏单例
private Singleton2(){
if(instance !=null){
throw new RuntimeException("正在实例化对象");
}
}