0. 单例模式
- 定义:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
- 要素:私有构造方法;指向自己实例的静态引用;以自己实例为返回值的静态共有方法;
- 分类:饿汉式(类加载时就实例化);懒汉式(调用时实例化);
- 优点:节省内存空间;避免频繁的创建和销毁,提高性能;避免对共享资源的多重占用;可以全局访问;
- 适用场景:需要频繁实例化并销毁的对象;创建对象开销大的对象;有状态的工具对象;频繁访问数据库或文件的对象;
- 具体场景:定时器;网页访问人数统计;电脑打印程序;生产唯一序列号;WEB中的计数器;数据库连接;
- 注意:除非认为地断开单例中静态引用到单例对象的关联否则JVM不会回收单例对象;使用反射机制会破坏单例机制,可以通过在构造函数中进行判断来避免或者使用枚举类;
总的来看:推荐使用静态内部类实现方式、双检锁实现方式,如果有特殊需求严格保证单例不被破坏需要使用枚举方式。
1. 一些实现方式
懒汉式,线程不安全
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
懒汉式,线程安全
public class Singleton {
private static Singleton instance;
private Singleton (){}
// 通过加锁的方式避免多个线程同时创建多个对象
// 这种方式有缺点:其实只需要在第一次创建的时候加锁,如果实例存在后面就不需要加锁
// 所以可以通过双检锁的方式进行改进
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
双检锁,线程安全
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
// 只有当实例为空的时候才进行加锁
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
饿汉式,线程不安全
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
静态内部类,线程安全
// 该方式一方面达到的双检锁的效果
// 另一方面实现了延迟加载,是比较理想的实现方式
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
枚举方式,线程安全,避免反射破坏
// 虽然可以在前面方法的构造方法中加入一些判断条件,例如实例如果存在,再次调用构造函数就报错
// 但是针对一些复杂的场景还是很难真正保证单例的,而枚举可以很好的保证
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}