单例模式
单例模式是Java中最简单的设计模式,属于创建型模式。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有的单个对象被创建。这个类提供了一种访问其唯一对象的方式,可直接访问,不需要实例化改类的对象。
核心就是 私有类构造器(保证只有在内部可以实例化),同时暴露对外的获取方法。
笔者认为,实现单例的方向主要分为两大类。 其一是通过多线程+锁的机制,确保同一时刻只有一个线程可以进行初始化操作 其二是利用classloader机制确保实例的唯一性。
多线程 + 锁:
这种情况下,单例又有两种模式,饿汉单例和懒汉单例。
**饿汉单例:**不管实例是否会被使用,在变量定义的时候,会被直接初始化。这样保证实例只有在类加载的时候会被初始化一次,以此实现单例。
class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return singleton;
}
}
**懒汉单例:**懒加载机制(lazy loading),类似kotlin中的by lazy ,只有在需要的时候才会进行初始化。
class Singleton {
private static Singleton singleton;
private Singleton () {}
public static Singleton getInstance() {
if (singleton == null)
singleton = new Singleton();
return singleton;
}
}
这种情况固然可以实现单例,但是在高并发的场景下,若有不同的线程同时访问getInstance方法,则可以new不止一次,造成不同的实例出现。接下来,我们对上诉Demo进行优化。
**优化方案一:**类上加锁
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
实现单例的代价太大了,每次加载类都需要上锁。但是在实际业务开发中,同步的场景很少,有点影响程序效率。
**优化方案二:**双重校验锁
通过synchronize 和 volatile 双重加锁,synchronize 保证了同一时间内有且只有一个线程可以进行初始化操作,volatile 不同线程访问的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;
}
}
静态内部类(利用classLoader机制)
懒加载机制,类加载的时候并没有进行初始化,只有在需要的时候才会加载SingletonHolder
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
|