一、什么叫单例模式
单例模式:能保证某个类在程序中只存在唯一一份实例
,
而不会创建出多个实例。
单例模式具体的实现方式
,
分成
"
饿汉
"
和
"
懒汉
"
两种。
饿汉模式: 在类加载的过程中同时创建实例。就相当于吃完饭以后即刻去洗碗,以防下次需要使用时找不到碗。
懒汉模式:
类加载的时候不创建实例
.
第一次使用的时候才创建实例。就相当于吃完饭后不用即刻去洗碗,等下次需要的时候用的时候再去洗。
饿汉模式:天然就是安全的,只是读操作
懒汉模式:不安全的,有读操作也有写操作
二、实现单例模式
2.1 饿汉模式
利用使类的构造方法私有化,防止重新创造实例,因此在此模式中有且仅有一个对象。
实现代码如下:
class Singleton{
// 唯一实例的本体
// volatile禁止指令重排序,保证后续线程肯定拿到的是完整对象
volatile private static Singleton instance = new Singleton();
// 获取实例的方法
public static Singleton getInstance(){
return instance;
}
// 禁止外部NWE实例 保证单例特性
private Singleton(){
}
}
public static void main(String[] args) {
// 此时S1和S2是同一个对象
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
}
2.2 懒汉模式
因为在多线程的条件下,无法只使用一个实例对象,而饿汉模式将对象具体化,只允许存在一个实例对象,因此懒汉模式在每一个线程执行时为每一条线程任务创造一个实例对象。
具体实现代码如下:
class SingletonLazy{
private static SingletonLazy instance = null;
// 加锁操作
// synchronized public static SingletonLazy getInstance(){
// if(instance == null){
// instance = new SingletonLazy();
// }
// return instance;
// }
// 加锁操作
// public static SingletonLazy getInstance(){
// synchronized (SingletonLazy.class){
// if(instance == null){
// instance = new SingletonLazy();
// }
// return instance;
// }
// }
public static SingletonLazy getInstance(){
// 这个条件,判定是否要加锁,如果对象已经有了,就不必加锁了,此时线程是安全的
if(instance == null){
synchronized (SingletonLazy.class){
if(instance == null) {
instance = new SingletonLazy();
}
}
}
return instance;
}
private SingletonLazy(){
}
}
public static void main(String[] args) {
SingletonLazy s1 = SingletonLazy.getInstance();
SingletonLazy s2 = SingletonLazy.getInstance();
System.out.println(s1 == s2);
}
运行结果:
3.总结
加锁 / 解锁是一件开销比较高的事情. 而懒汉模式的线程不安全只是发生在首次创建实例的时候.
因此后续使用的时候, 不必再进行加锁了.
外层的 if 就是判定下看当前是否已经把 instance 实例创建出来了.
同时为了避免 "内存可见性" 导致读取的 instance 出现偏差, 于是补充上 volatile .
当多线程首次调用 getInstance, 大家可能都发现 instance 为 null, 于是又继续往下执行来竞争锁,
其中竞争成功的线程, 再完成创建实例的操作.
当这个实例创建完了之后, 其他竞争到锁的线程就被里层 if 挡住了. 也就不会继续创建其他实例.