单例的四种实现方式
一、饿汉方式
public class Singleton{
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance() {
return instance;
}
}
- 优点:代码简单,避免多线程同步问题
- 缺点:类的初始化阶段就会导致实例化,无法延迟实例化。
触发类进行初始化条件
- 使用new关键字创建一个对象
- 读取/设置一个类的静态字段
- 使用反射调用类
- 初始化一个类时候,如果其父类还未被初始化,会优先触发其父类初始化
- 当jvm启动时,用于指定执行的主类
二、懒汉方式(DCL)
public class Singleton {
private static volatile Singleton singleton = null;
private Singleton(){}
public static Singleton getSingleton(){
if(singleton == null){
synchronized (Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
- 优点:延迟单例实例化,以DCL方式提高线程并发效率
- 注意:JDK1.5引入关键字volatile才能达到正常单例效果。对一个volatile域的写,happens- before 于任意后续对这个volatile域的读,即保证了读写操作不会乱序。
三、静态内部类
public class Singleton {
/**
* 是静态内部类,该内部类的实例与外部类的实例没有绑定关系,
* 而且只有被调用到才会装载,从而实现了延迟加载
*/
private static class SingletonHolder {
//保证了线程安全
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
- 优点1:线程安全——利用了classloader机制保证只有一个线程来初始化类。
- 优点2:延迟实例化——只有通过getInstance方法才会触发内部类被初始化。
四、枚举
public enum Singleton {
INSTANCE;
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
}
在JDK1.5加入enum特性
使用enum实现单例优点:
- 线程安全(构造方法)——无序担心DCL带来的影响
- 不因为序列化而产生新实例
- 防止反射攻击