单例模式(线程安全)

目录

一 . 什么是单例模式 

    饿汉与懒汉模式介绍 : 

二 . 饿汉模式

三 . 懒汉模式


一 . 什么是单例模式 

单例模式 ---> 是一种经典的设计模式.(设计模式 : 类似于"棋谱" , java中的大佬们针对一些工作中常见的场景,总结出来的编写"套路").  

单例 : 指的是单个实例(对象) , 一个程序中, 某个类,只可以创建出一个实例(对象),不能创建多个对象.

Java中的单例模式 , 通过借助Java语法, 保证某个类, 只能够创建出一个实例, 不能new出多个类.

Java 中的两个经典单例模式 : 

1 . 饿汉模式

2 . 懒汉模式 

    饿汉与懒汉模式介绍 : 

饿汉 : 

举个栗子吧 : 好比平常的脏衣服, 穿完后, 直接洗掉.   (急迫)

懒汉 : 

举个栗子 : 好比平常的脏衣服 , 脏了脱掉之后, 先不洗, 等下次没有干净衣服穿的时候, 我再去洗.  (从容)

上述这两个栗子 : 放在现实生活中 , 肯定是饿汉更好, 脏了就洗, 不然衣服放哪儿都臭了.

而在计算机中 通常认为懒汉模式更好,效率更高  (非必要,不洗)

例如 : 打开一个硬盘上的文件,读取文件的内容,并显示出来.

          饿汉 : 把文件所有内容都读到内存中,并显示

          懒汉 : 只把文件读一小部分 , 把当前屏幕填充满 , 如果要翻页, 再去读其他文件内容, 如若不翻页, 那么内存就剩下来了.

如果文件非常大 10G !!!!!    采用饿汉方式, 文件打开可能要卡半天, 内存够不够还不知道.

                                        如果采用懒汉, 每次我们加载一点,即不影响内存,也不影响我们去读.

二 . 饿汉模式

先看代码实现 : 

public class Singleton {
    // 创建单一模式对象
    private static Singleton instance = new Singleton();
    //将构造方法的权限设置为private 为了确保单一模式实例的唯一性.
    private Singleton() {}
    //提供一个get方法  获取单一变量
    public static Singleton getInstance() {
        return instance;
    }
}

测试类 :  

public class Test {
    public static void main(String[] args) {
        Singleton instance = Singleton.getInstance();
        System.out.println(instance);
    }
}

单例模式(线程安全)_第1张图片

饿汉模式 : 在想使用这个对象时, 直接使用这个早都准备好的类成员.

饿汉模式是不是线程安全的?

假设如果多个线程都去调用getInstance()

由于是进行读操作, 因此认为饿汉模式是线程安全的.

三 . 懒汉模式

懒汉模式 : 非必要, 不创建

因此创建对象就是在调用getInstance时进行创建的.

先看这个代码 : 

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

分析 : 等其他类去调用getInstance方法时 , 先判断instance是不是空的,如果为空,那么就创建一个对象, 如果不为空那么就返回当前的instance对象.

但 : 思考一个问题 , 如果在多个线程下呢? 

单例模式(线程安全)_第2张图片

 因此为了确保线程安全 , 就要进行加锁操作.

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

问题又来了 : 

换个思考方向 : 因为instance 只需要被创建一次 , 加锁针对的是在instance == null 的情况下,多个线程同一时间执行 , 以防创建出多个对象 , 等instance创建出来之后 , 锁竞争还是存在,这样就影响了程序的执行效率, 后面的线程想要获得instance 对象 , 都要进行加锁解锁操作, 这是没有必要的, 因为instance 早已经创建出来了. 

因此我们在加锁前面在加上一个 if 判定.

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

到这里还没有结束 : 

指令重排序!!!!

单例模式(线程安全)_第3张图片

解决 : 加上volatile 关键字

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

你可能感兴趣的:(JAVAEE,单例模式,java,开发语言)