1.什么是单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
2.立即加载与延时加载
2.1立即加载:使用对象的时候对象已经创建完毕。
优点:首次使用不用再花时间去创建对象。
缺点:如果对象创建后不使用,则浪费了内存资源。
2.2延时加载:在使用对象的时候采取创建对象。
优点:使用的时候才去创建,不浪费内存资源。
缺点:首次使用是花费创建对象,需要消耗时间。
3.单例模式的几种实现
3.1使用静态变量实现
public class Singleton01 {
private static Singleton01 singleton01 = new Singleton01();
public static Singleton01 getInstance() {
return singleton01;
}
}
总结:静态初始化器,由JVM来保证线程安全,在系统初始化时就创建了对象。在实际业务开发中,是在系统启动时就创建对象还是要在用到的时候再去创建要根据具体的业务场景来选择恰当的方式。
3.2使用synchronized关键字实现
public class Singleton02 {
private static Singleton02 singleton02 = null;
public static synchronized Singleton02 getInstance() {
if (singleton02 == null) {
singleton02 = new Singleton02();
}
return singleton02;
}
}
总结:通过使用synchronized修饰的静态方法,在首次使用的时候创建对象。synchronized保证了创建和获取对象的安全性,但是这种安全也是有代价的,当处于高并发的场景下会导致同一时刻只有一个线程获得锁,会降低代码的运行效率。
3.3使用DCL双锁检查实现
private static volatile Singleton03 singleton03 = null;
public static Singleton03 getInstance() {
if (singleton03 == null) {
synchronized (Singleton03.class) {
if (singleton03 == null) {
singleton03 = new Singleton03();
//可能这里再进行一些初始化操作
}
}
}
return singleton03;
}
总结:通过使用volatile关键字和2次检查和synchronized代码块,在一定程度上确保了创建对象的安全性。其实在高并发场景下可能会出现对象还没有初始化完成就已经被外部使用了,这也是一种不安全。在对象初始化完成后,并发的获取对象会比3.2的效率高。
3.4使用静态内部类实现
public class Singleton04 {
private static class Singleton04Holder {
private static Singleton04 singleton04 = new Singleton04();
}
public static Singleton04 getInstance() {
return Singleton04Holder.singleton04;
}
}
总结:类级的内部类,也就是静态的成员内部类,该内部类的实例与外部类的实例,没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载。
3.5使用枚举实现
public enum Singleton05 {
INSTANCE;
private Object singleton;
Singleton05() {
this.singleton = new Object();
}
public Object getInstence() {
return singleton;
}
}
总结:枚举实例的创建是线程安全的,并且在任何情况下都是单例。
4.使用场景举例:
封装不可变的配置信息的类;
线程池/连接池;
用于请求第三方的客户端;