JavaEE-单例模式的写法(保证线程安全)

首先,需要知道啥是单例模式O.o?

顾名思义,也就是对应一个类而言,只创建出一个实例对象.这便是单例(单个实例).

在很多场景下,都要求单例模式,比如说JDBC的编写,DateSource实例就要求只有一个.

单例模式同时也是在校招中最乐意考的一种模式.

事实上,单例模式通常有两种常见实现方式:1)饿汉模式2)懒汉模式

这里给出一个栗子来理解啥是饿汉模式,啥又是懒汉模式:

比如说吃饭后需要洗碗,

饿汉:吃完之后,就会立即去洗碗.

懒汉:吃完之后,先把碗放一边,等到下一顿吃的时候,需要用到碗了再洗.
又比如,中午吃饭,用了 4 个碗.
饿汉,就得把 4 个碗都洗了.
懒汉,晚上吃饭只用 2 个碗,此时就只需要洗 2 个就行了~~
通常认为,懒汉模式,更好,效率更高(非必要,不洗碗).

对应到计算机文件中,这里也给出一个栗子:

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

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

懒汉: 只把文件读一小部分,把当前屏幕填充上,如果用户翻页了,再读其他文件内容,如果不翻页,就省下了.

假设文件非常大 10 G!!

饿汉方式文件打开可能要卡半天!!内存够不够 都不知道了!!懒汉模式就可以快速打开!!
那你是愿意刚文件就显示内容了,还是一直傻等着到10G的文件全部被加载好再看??

因此,通常更推荐采用懒汉模式来实现单例模式.

但是不幸的是,懒汉模式并不是线程安全的,因此需要人为加锁并添加volatile来修饰变量,以此来达到线程安全的目的.

下面来分别说明饿汉模式和懒汉模式的代码实现:

饿汉模式:

类加载的时候,就创建好了实例对象.

class Singleton {
    private static Singleton instance = new Singleton();   //唯一实例对象
    private Singleton() {}   //构造方法用private修饰,禁止类外再new实例
    public static Singleton getInstance() {   //获取唯一实例
        return instance;
   }
}

由于饿汉模式只涉及读操作,因此是线程安全的.但奈何效率不如饿汉模式.

懒汉模式:

非必要不创建,只有在需要对象的时候,才去创建实例对象.

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.双重if判断,避免重复加锁.毕竟加锁本身也是一件麻烦的事,加锁就要涉及解锁,也就会导致线程阻塞,效率低且占用资源.

2.volatile修饰变量禁止指令重排序,保证后续线程拿到的是完整的对象.由于涉及new操作,new操作是三部曲(在之前我的博客线程安全里有说过,可以去查看),因此可能会导致多线程环境下出现异常.

3.使用synchronized加锁,将new操作和if操作变成原子的.避免在多线程环境下,由于线程的无序调度而导致同时创建出多个对象.

说明一点:此处是否涉及内存可见性的问题,还有待商榷.

以上便是本节的全部内容.

uu们加油呀!!!

JavaEE-单例模式的写法(保证线程安全)_第1张图片

 

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