相信大家去面试的时候,经常被问到单例模式的有关问题吧,今天我们就来好好总结一下
一 懒汉式
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
相信大家对这段代码再熟悉不过了,懒汉式的意思就是每次在调用getInstance方法时,才去new一个Singleton的实例,但是很明显,按上面这样写有问题啊,线程不同步啊,那么怎么办呢
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
在getInstance方法之前加上synchronized不就解决了,但是这样,每次调用getInstance都会同步,而我们只是希望在第一次new Singleton()的时候同步即可,一旦instance不为空了,就不用再同步了,那么怎么做呢
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
在代码块利用锁,并且要加上双重检测,因为两个线程很可能同时执行到第一个判空的地方,可能判断都为空,就都会进入下面的代码里,就会生成两个对象,所以要加上双重锁定,但是这样就没问题了吗,当然不是,我们都知道java内存模型中有无序写的机制,instance = new Singleton(); 这行其实做了两个事情:1、调用构造方法,创建了一个实例。2、把这个实例赋值给instance这个实例变量。可问题就是,这两步jvm是不保证顺序的。也就是说。可能在调用构造方法之前,instance已经被设置为非空了
比如顺序被重排了,先保证instance 不为null,后面才调用构造函数初始化,这个时候另外一个线程判断instance 不为空,直接返回instance ,这样就会有问题,那么怎么解决这个问题呢,我们知道 volatile关键字可以在instance 赋值的时候,禁止重排序
public class Singleton {
private volatile static Singleton instance; //声明成 volatile
private Singleton (){}
public static Singleton getSingleton() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
这样这个问题就解决了,但是是不是觉得很累,下面给出一种完美的解决方法,利用静态内部类
public class Singleton {
private static class SingletonHolder{
private static Singleton instance = new Singleton();
}
private Singleton() {
}
//获取单例对象实例
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
我们知道,静态内部类只有在getInstance里第一次被调用的时候才加载,实现了懒加载,而且加载过程是线程安全的,所以一般用这种方法来实现懒汉式单例子
二 饿汉式
//饿汉式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
在类加载的时候就new好对象,这样也不存在线程安全问题,但是饿汉式也有问题,如果构造的单例很大,构造完又迟迟不使用,会导致资源浪费。
总结:推荐使用懒汉式静态内部类的方式实现单例模式,好了,设计模式-单例模式就总结到这里,如有问题,欢迎指正,谢谢。