一:概述
所谓单例模式,就是一个类只一个实例对象,该实例对象具有唯一性,外部访问到的对象都是同一对象实例
二:实现思路
1.构造函数私有化,确保外部通过new关键字无法创建新的实例对象
2.要有一个全局变量供外部访问(可以是静态变量)
3.要在本类创建出实例对象,确保有对象实例
三:类型
单例模式分为饿汉模式与懒汉模式:
1.饿汉模式:饿了就要吃,即任何时候访问都要有对象准备好随时被访问,可以在类初始化的时候创建该对象;
public class Singo{
private Singo(){} // 构造函数私有化,禁止外部创建对象
private static instance = new Singo();
public static Singo getInstance(){
return instance;
}
}
2.懒汉模式:只有在调用的时候对象才会被创建,例如把创建实例放在一个方法里,那么只有调用该方法的时候该对象才会被创建
public class Singo{
private Singo(){} // 构造函数私有化,禁止外部创建对象
private static instance;
public static Singo getInstance(){
if(this.instance==null){
instance = new Singo();
}
return this.instance
}
}
四:多线程下的单例模式
显然,在多线程的情况下,上述单例模式的实现是无法保证单例的。比如懒汉模式中,线程A进入 if语句块,但是在new对象前,若线程B访问 if(this.instance==null)一样会返回true,这样一来,就相当于new了两个实例对象,显然是不符合单例模式的,那么如何在多线程的场景下实现单例模式呢?
1.使用synchronized锁住整个方法(锁住整个方法,效率低下。每一次调用getInstance都要获取锁,即使实例已经被创建过了还要获取锁)
public synchronized Singo getInstance(){
if(instance==null){
instance = new Singo();
}
return instance
}
2.使用synchronized锁住代码片段
public Singo getInstance(){
if(instance==null){
synchronized(Singo.class){
instance = new Singo();
}
}
return instance
}
实际还是不安全的,会面临和单线程一样的问题
优化:双重校验(依然会有缺点,因为jvm有可能指令重排,因此使用volatile修饰变量禁止指令重排)
private volatile Singo instance;
public Singo getInstance(){
if(instance==null){
synchronized(Singo.class){
if(instance==null){
instance = new Singo();
}
}
}
return instance
}
另外,还可以使用枚举类,静态内部类来实现(枚举类里一个元素就是一个实例)