单例设计模式是平时我们在项目开发中最常用的一种设计模式,一般情况下在频繁创建销毁对象时候,我们会想到用单例对象去处理,比如像数据库连接池对象,线程池对象,日志对象,等等。
那么我们在开发的时候怎么去实现单例模式呢?我们都知道单例模式通常分为懒汉和饿汉模式。为了代码效率更高通常我们会选择懒汉模式,调用的时候才会去创建对象 而不是类一加载去创建。
以下这几种懒汉模式我们通常会在项目中使用:
1、双重检锁,保证在创建对象的时候只有一个线程创建对象,为什么需要双重检锁呢?这是由于创建对象的时候 JVM 会依次执行类加载检查、对象分配内存、并发处理、内存空间初始化、对象设置、执行ini方法。所以我们必须得保证同一时刻只有一个线程进行创建对象,volatile关键字 关键作用在于 禁止指令重排序,保证指令顺序执行。内存可见性 保证主内存和工作内存保持同步,其他线程能得到最新的值。
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}
2、通过静态内部类的方式去实现,一般情况下我们使用这种方式最为普遍。实现起来比较简单,JVM保证静态内部类初始化只执⾏⼀次,所以线程安全,另外我们调用的时候才会去创建,所以非常理想的一种实现方式。
public class Singleton {
private Singleton() {
}
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
3、通过枚举实现,我们都知道枚举类型是线程安全的,只会装载⼀次,所以是最适合实现单例对象。如果说要考虑反射和反序列化破坏单例,Joshua Bloch建议 采用该方式,并且重写反序列化⽅法readResolve()。
public class Singleton {
private Singleton() {
}
private enum InnerSingleton {
INSTANCE;
private final Singleton instance;
InnerSingleton() {
instance = new Singleton();
}
private Singleton getInstance() {
return instance;
}
}
public static Singleton getInstance() {
return InnerSingleton.INSTANCE.getInstance();
}
}
4、通过容器框架实现,最常见的就是Spring IOC,这是最常见的实现方式。当然我们也可以采用Map实现,通常我们使用Map实现简单的缓存类的时候采用。
public class Singleton {
private static Map<String, Object> map = new HashMap<>();
public static void addCache(String key, Object value) {
if (!map.containsKey(key)) {
map.put(key, value);
}
}
public static Object getCache(String key) {
return map.get(key);
}
}