目录
一、单例模式的定义
单例模式是指在内存中只会创建一次对象的设计模式。在程序多次使用同一对象时,为了防止频繁的创建对象而增加资源的开销,但是单例模式就可以让程序在内存中只创建一个对象,让所有需要调用的地方都共享这一单例对象。
二、单例模式的具体实现方式
单例模式的两种类型:饿汉模式和懒汉模式
2.1 饿汉模式
类加载的同时,创建实例
主要实现步骤:
1、先将构造方法私有化(private),避免直接在外部new多个对象。
2、在类的内部属性直接创建一个对象。
3、向外部提供一个公共的静态方法,用于外部引用该对象。
注意:之所以用静态方法(static),是因为类外无法直接创建对象,只能通过类名获取该类的属性和方法。
代码演示:
public class Singleton {
private static Singleton instance = new Singleton();
//获取实例的方法
public static Singleton getInstance() {
return instance;
}
//禁止外部new实例
private Singleton() { }
}
2.2 懒汉模式
类加载的时候不创建实例,第一次使用的时候才创建实例。
主要实现步骤:
1、先将构造方法私有化(private),避免直接在外部new多个对象。
2、在类的内部先声明一个对象的引用,但是不创建。
3、向外部提供一个公共的静态方法,用于创建对象并返回对象的引用。
代码演示:
class SingletonLazy {
private static SingletonLazy instance;
public static SingletonLazy getInstance() {
if(instance == null) {
instance = new SingletonLazy();
}
return instance;
}
private SingletonLazy() {
}
}
注意:此代码存在严重的线程安全问题
三、线程安全问题
懒汉模式会带来线程安全问题
在懒汉模式代码中,既包含了读也包含写,两步操作不是原子性导致线程不安全,所以存在线程安全问题。
举例说明:
假如有两个线程t1和t2,当t1和t2都进入该方法时,进行判断都为null,那就导致t1和t2指向的不是同一个对象了,所以就导致了线程不安全了。
解决方案:
我们可以进行加锁操作(synchronized),这样就可以保证一个线程在进行读和写操作的时候,别的线程就在锁外进行阻塞等待,这样就保证操作是原子性的了。
代码演示:
public static SingletonLazy getInstance() {
synchronized (SingletonLazy.class) {
if (instance == null) {
instance = new SingletonLazy();
}
}
return instance;
}
但是问题真的解决了嘛?
对于加锁操作来说,加锁确实可以保证线程安全,但是频繁的加锁是需要开销的,资源会浪费。
那有什么办法可以避免频发的加锁呢?
我们可以在外面再加一层if条件判断,判断是否instance被初始化完成,若被初始化完成就不必加锁了。
代码演示:
public static SingletonLazy getInstance() {
if(instance == null) {
synchronized (SingletonLazy.class) {
if (instance == null) {
instance = new SingletonLazy();
}
}
}
return instance;
}
现在代码貌似没有问题了,但是还会存在一个内存可见性问题!
如果多个线程都去调用getInstance()方法,就会造成频繁的读instance操作,但是对于读内存和读寄存器来说,读寄存器会更高效,因此编译器会自动优化在寄存器读,也就是说即使某个线程将instance初始化了,但是由于编译器自动优化了,其它线程看起来instance还是null。
解决方案:
我们可以用volatile对instance进行修饰,被volatile修饰的变量,都会从内存中读取该变量,volatile可以禁止指令重排序!
最终代码演示:
class SingletonLazy {
volatile private static SingletonLazy instance;
public static SingletonLazy getInstance() {
if(instance == null) {
synchronized (SingletonLazy.class) {
if (instance == null) {
instance = new SingletonLazy();
}
}
}
return instance;
}
private SingletonLazy() {
}
}