Java-单例模式(饿汉模式与懒汉模式)

目录

一.饿汉模式

二.懒汉模式(推荐)

1.单线程版本

2.多线程版本

3.多线程版本plus 

 ​​​​​​​


说明:
单例模式能保证某个类在程序中只存在唯一一份实例 , 而不会创建出多个实例 .
单例模式具体的实现方式 , 分成 " 饿汉 " " 懒汉 " 两种 .

一.饿汉模式

类加载的同时 , 创建实例 .
class Singleton {
    private static Singleton instance = new Singleton();//创建唯一对象
    private Singleton() {}//使类不能实例化对象
    public static Singleton getInstance() {//调用此方法可获取对象
        return instance;
   }
}

二.懒汉模式(推荐)

1.单线程版本

 类加载的时候不创建实例. 第一次使用的时候才创建实例.

 相较于饿汉模式,懒汉模式则高效不少,想用的时候才创建,不想用就不创建.而饿汉模式一开始就创建完了,不管你用还是不用.所以我们更推荐使用懒汉模式.

class Singleton {
    private static Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
       }
        return instance;
   }
}

但是,如果是在多线程情况下,我们就必须考虑到线程的安全性,

饿汉模式的代码只有读操作,而读操作是安全操作,因此饿汉模式在多线程下是安全的,

反观懒汉模式里有new对象(修改操作),修改操作是不安全的操作,懒汉模式在多线程下是不安全的.所以我们需要优化我们代码,使它在多线程下也是安全的.

2.多线程版本

 加上 synchronized可以改善这里的线程安全问题.

class Singleton {
    private static Singleton instance = null;
    private Singleton() {}
    public synchronized static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
       }
        return instance;
   }
}

我们知道只有new的时候才出现线程不安全问题,因为它是修改操作,一旦对象new好了,后续再调用getInstance方法仅仅是读操作,这时候线程是安全,就用不到上锁了,那么可不可以不锁整个方法,只锁部分代码,从而提高效率呢.

3.多线程版本plus 

在 多线程版本下做出的进一步改动:
1.使用双重 if 判定 , 降低锁竞争的频率 .
2.给 instance 加上了 volatile.
class Singleton {
    private static volatile Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
           if (instance == null) {
               instance = new Singleton();
               }
           }
       }
        return instance;
   }
}

解析: 

1.加锁 / 解锁是一件开销比较高的事情. 而懒汉模式的线程不安全只是发生在首次创建实例的时候. 因此后续使用的时候, 不必再进行加锁了.
2.外层的 if 就是判定下看当前是否已经把 instance 实例创建出来了.
3.同时为了避免 "内存可见性" 导致读取的 instance 出现偏差, 于是补充上 volatile .
4.当多线程首次调用 getInstance, 大家可能都发现 instance null, 于是又继续往下执行来竞争锁, 其中竞争成功的线程, 再完成创建实例的操作. 当这个实例创建完了之后, 其他竞争到锁的线程就被里层 if 挡住了. 也就不会继续创建其他实例.
5.你可以这样理解,外层的if语句是判断是否要加锁:首次创建实例的时候加锁,其他的不用加锁,
内层则是判断是否是第一次创建实例:第一次就new对象,不是就不new对象.
虽然两次代码的判断条件是一样,但是代码的初心是不一样.

你可能感兴趣的:(单例模式,java)