单例模式是我们非常常用的设计模式之一。百度百科给出的定义:
单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要,也有可能一个线程中属于单例,如:仅线程上下文内使用同一个实例)
一般,我们为了实现单例模式,通常把构造器私有化,然后通过静态方法和静态变量来获取一个对象。
这种方式是最基础的实现方式,最大的问题是不支持多线程,在多线程模式下会产生线程安全问题。
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
所以我们给getInstance()加synchronized锁,保证它在多线程的情况下使用。但会影响效率。
public class Singleton {
private Singleton singleton;
private Singleton(){};
public synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
比较常用的一种方法,使用classLoder机制避免了多线程同步问题。但是会产生垃圾对象,浪费内存。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance() {
return instance;
}
}
采用懒加载模式,同样也是线程安全的。采用双锁机制,在多线程模式下也能保持高性能
/**
* 懒汉式单例模式(线程不安全)
* */
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
上面双重校验锁可能会出现如下线程问题:
在 Singleton 构造函数体执行之前,变量 instance 可能成为非 null 的,即赋值语句在对象实例化之前调用,此时别的线程得到的是一个还会初始化的对象, 也就是 一个线程走到3正在初始化时被挂起,另一个线程走到0,发现instance不为null,就直接返回一个没有初始化完全的对象!
所以为了避免这种情况发生,我们给instance加上volatile关键字,利用它禁止指令重排序的功能,给写操作添加一个内存屏障。即在完全的初始化完一个对象前,不会调用读操作。
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;
}
}
和饿汉式同样,使用ClassLoader机制来保证了初始化instance只有一个线程。而和饿汉式不一样的地方在于,使用了Lazy初始化,只有在主动调用getInstance方法时,才会实例化对象。
/**
* 懒汉式单例模式(线程不安全)
* */
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton() {}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
采用单例模式的最佳方法,简介、自动支持序列化机制,而且避免了线程同步问题。不过目前很少被使用。
public enum Singleton {
INSTANCE;
public void whateverMethod() {}
}
ThreadLocal为每个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。
/**
* 使用ThreadLocal实现单例模式(线程安全)
* */
public class Singleton {
private static final ThreadLocal<Singleton> tlSingleton = new ThreadLocal<Singleton>() {
@Override
protected Singleton initialValue() {
return new Singleton();
}
};
private Singleton(){}
public static Singleton getInstance() {
return tlSingleton.get();
}
}
import java.util.concurrent.atomic.AtomicReference;
/**
* CAS
* */
public class Singleton {
private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<>();
private Singleton(){}
public static Singleton getInstance() {
for (;;) {
Singleton current = INSTANCE.get();
if (current != null) {
return current;
}
current = new Singleton();
if (INSTANCE.compareAndSet(null , current)) {
return current;
}
}
}
}