首先,需要知道啥是单例模式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们加油呀!!!