单例模式,顾名思义就是只有一个实例,并且她自己负责创建自己的对象,这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。 属于创建型模式。
1.1懒汉式,线程不安全
懒汉式分为 线程安全 和 线程不安全两种方式,区别在于是否添加了 synchronized 关键字。
懒汉式,因为比较 懒,所以只有在调用的时候才会去判断有没有实例,如果已经实例则返回,反之则新建。
public class Singleton {
private static Singleton instance = null;
//私有的默认构造函数
private Singleton() {
}
//静态工厂方法
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
1.2懒汉式,线程安全
线程安全
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
public class Singleton {
private static Singleton instance = null;
//私有的默认构造函数
private Singleton() {
}
//静态工厂方法
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
2 饿汉式
饿汉式,实例在初始化时就已经创建好了,不管是否用到,都先创建实例。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
public class Singleton {
private static final Singleton instance = new Singleton();
//私有的默认构造函数
private Singleton() {
}
//静态工厂方法
public static Singleton getInstance() {
return instance;
}
}
3 双检锁/双重校验锁(DCL,即 double-checked locking)
双检锁,又叫双重校验锁,综合了懒汉式和饿汉式两者的优缺点整合而成。
特点是在synchronized关键字内外都加了一层 if 条件判断,这样既保证了线程安全,又比直接上锁提高了执行效率,还节省了内存空间。
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
当一个线程发出请求后,会先检查instance是否为null,如果不是则直接返回其内容,这样避免了进入synchronized块所需要花费的资源。
4 静态内部类
静态内部类的方式效果类似双检锁,但实现更简单。但这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
public class Singleton {
//私有的默认构造函数
private Singleton() {
}
//内部类
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
//静态工厂方法
public static Singleton getInstance() {
return SingletonHolder.INSTANCE ;
}
}
5 枚举
这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
总结
一般情况下,不建议使用 懒汉式,建议使用 饿汉式。
只有在要明确实现 lazy loading 效果时,才会使用静态内部类。
如果涉及到反序列化创建对象时,可以尝试使用枚举式。
如果有其他特殊的需求,可以考虑使用双检锁。