单例模式
定义
确保一个类只有一个实例,并且提供该实例的全局访问点,就是这个类只能被new一次,并且每次都是用的new的这个。
类结构
一个私有的构造方法,一个私有的静态的实例变量,一个公有的静态函数,用来获取实例。
私有构造方法:可以不让别人创建
共有的静态函数:该实例的全局访问点,返回唯一的私有的静态实例变量
具体实现
懒汉式-线程不安全
懒汉式就是用到的时候才创建返回对象,不用到的时候不会创建,但也存在线程不安全的问题
线程不安全:多个线程同时第一次访问获取对象,都判断为空,然后都new了对象,导致多次实例化对象
public class Singleton {
private static Singleton uniqueInstance;
private Singleton(){
}
public static Singleton getUniqueInstance(){
if (uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
懒汉式-线程安全
如何解决懒汉式的线程不安全问题呢?使其变为线程安全的,只需要在获取实例的时候加锁就行,这样同一时间只能有一个线程进入获取实例的方法,就可以避免线程不安全问题了!
缺点:由于加锁了,导致同一时间只能有一个线程访问这个方法,有性能问题,不建议使用。
public static synchronized Singleton getUniqueInstance(){
if (uniqueInstance == null){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
饿汉式-线程安全
也是为了解决懒汉式的线程不安全问题,直接实例化就行,不再使用延迟实例化,但是没有延迟实例化能节约资源
public class Singleton {
private static Singleton uniqueInstance = new Singleton();
private Singleton(){
}
public static synchronized Singleton getUniqueInstance(){
return uniqueInstance;
}
}
双重校验锁-线程安全
就是在实例化的时候进行加锁即可,不在方法上进行加锁了
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton(){
}
public static Singleton getUniqueInstance(){
if (uniqueInstance == null){
synchronized (Singleton.class){
if (uniqueInstance == null){
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
首先解释为什么用volatile关键字
禁止jvm的指令重排,首先实例化一个对象分为三个步骤
- 分配内存空间
- 初始化对象
- 将对象指向分配的内存地址
这个顺序在jvm中执行式有可能乱序的,比如会132这样执行,当一个线程执行完成13之后,另一个线程刚好进入if判断不为空,直接就返回了一个未初始化的对象,所以为了解决这个问题使用volatile关键字可以禁止指令重排
再解释为什么用两个if
当两个线程同时进入第一个if,都判断不为空,然后上锁,一个线程进行实例化对象操作,实例化完毕后,另一个线程也加锁进入,但是如果没有第二个if的话,第二个进程还会进行实例化,加上之后就不会进行第二次实例化了。
静态内部类实现-线程安全
就是使用一个静态内部类,类中写一个静态常量,Singleton类被加载的时候并不会调用内部类,只有需要获取实例的时候才会调用内部类进行获取。
实现了延迟实例化+线程安全
public class Singleton {
private Singleton(){
}
private static class SingletonHolder{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getUniqueInstance(){
return SingletonHolder.INSTANCE;
}
}