前言
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。
这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。
这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
场景:https://www.jianshu.com/p/91df47bb1212
定义
单例模式: 一个类有且只有一个实例,且提供一个全局访问方法来访问这个实例。
注:1、被static修饰的变量成为静态变量,当类加载器将类加载到JVM中的时候就会创建静态变量,这跟对象是否创建无关。
2、静态代码块:静态代码块的代码只会在类第一次初始化的时候执行一次。
3、静态变量创建后,会一直放在内存中,只有进程被杀掉时,才会被清空。这也是单例模式的核心。
单利模式
1、饿汉模式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public Singleton getInstance() {
return instance;
}
}
缺点是在类加载的时候就实例化了对象,有点浪费空间,优点是线程安全,因为static变量会在类装载的时候初始化,并且多个实例的static变量会共享一块内存区域。
2、懒汉模式
public class Singleton{
private static Singleton singleton= null;
private Singleton(){
}
public Singleton getInstance(){
if (singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
但是由于线程不安全,于是加上了synchronized关键字修饰方法
public class Singleton{
private static Singleton singleton= null;
private Singleton(){
}
public synchronized Singleton getInstance(){
if (singleton == null){
singleton = new Singleton();
}
return singleton;
}
}
加上锁以后又影响性能,于是又有了双锁检测(DCL)版本的单例模式(双重检查+锁)
public class Singleton {
private static volatile Singleton singleton = null;
private Singleton() {
}
public Singleton getInstance() {
if (singleton == null) {
synchronized (this) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
1.为什么要进行第一次判空
如果说我们没有第一次验校,每一个线程都要走synchronized 中的代码,而每一次线程都要去拿到同步锁才能执行。在多线程的情况下每一个线程要拿到single 对象都要排队等待同步锁释放。因此第一次验校作用就是为了提高程序的效率。
2.为什么要进行第二次判空
如果有两个线程A和B都通过了第一次判空,进入到获取锁阶段A拿到锁进行对象创建后释放锁,此时B就不需要在创建对象,所以再次判断不为空,直接返回。
3.变量为什么要加volatile关键字
volatile防止指令重排序,single = new Single() 在我们看来就是一句话操作而已,但在虚拟机看来它一共分为了几个指令操作:
- 为对象分配内存空间
- 初始化对象
- 将引用指向对象的内存空间地址
假如线程A执行 single = new Single()虚拟机是按132排序执行,当执行到3的时候single 引用已经不为空。此时若线程B执行到第一次验校处(第一次验校不在同步代码中,因此所有线程随时都可以访问),它判断 single ==null 得到false,直接返回single对象。但是此时single对象还没初始化完成,因此很有可能就会发生bug。