单例模式
一、什么是单例模式
单例模式是一种常见的设计模式,定义是这个类只允许有一个实例对象存在
二、使用场景
购买东西时的购物车,window系统的回收站等等
三、实现方式
1. 懒汉式
public class Lazy {
private static Lazy instance;
private Lazy(){}
public static Lazy getInstance(){
if(instance == null){
instance = new Lazy();
}
return instance;
}
}
懒汉式,实行延迟加载,不会初始化,在需要用到实例的时候才去创建,用到的时候先检查实例存不存在,存在就返回,不存在创建一个返回,可以再单线程下使用,多线程下是不安全的,一个线程通过了判空判断的同时,另一个线程也通过了判空判断,就会同时创建多个实例,想要线程安全可以加synchronized关键字,但是会影响效率,不推荐。
- 优点:延迟加载,不会浪费内存
- 缺点:线程不安全,加synchronized会影响效率
2. 饿汉式
public class Hungry {
private static Hungry instance = new Hungry();
private Hungry(){}
public static Hungry getInstance(){
return instance;
}
}
饿汉式,在初始化的时候创建实例,不会存在线程安全问题
- 优点:线程安全
- 缺点:没有延迟加载效果,如果没有用到这个实例就会浪费内存
3. 双检锁
public class DoubleCheck{
private volatile static DoubleCheck instance;
private DoubleCheck(){}
public static DoubleCheck getInstance(){
if(instance == null){
synchronized(DoubleCheck.class){
if(instance == null){
instance = new DoubleCheck();
}
}
}
return instance;
}
}
双检锁,外面一层if判断对象存在就不会执行加锁代码,提高了效率,synchronized加上里面一层 if 保证了线程安全
- 优点: 线程安全,延迟加载,效率高,推荐
为什么加volatile关键字
原因是 instance = new DoubleCheck(); 这句代码不是原子性的。
创建一个对象分为三步:
- 分配 instance 对象内存空间 N
- 在内存 N 初始化对象
- 把 N 的地址赋值给对象 instance
这时在实例instance指向N的时候,instance是不为空的
但是,编译时编译器可能会将2,3顺序重新排序,造成顺序为1-3-2
- 分配 instance 对象内存空间 N
- 把 N 的地址赋值给对象 instance
- 在内存 N 初始化对象
线程A,在内存 N 初始化对象之前就将 N 的地址赋值给了instance
这时线程B调用了getInstance方法,发现instance不为null
这个时候instance没有初始化对象,线程B会将这个未初始化的对象返回,线程B在使用instance对象时就会出现问题
使用volatile修饰instance可以防止指令重排序,就可以避免这种现象发生。
4. 静态内部类
public static class Single{
private static class Singleton{
private static final Singleton INSTANCE = new Singleton();
}
private Singleton(){}
public static final Singleton getInstance(){
return Singleton.INSTANCE;
}
}
静态内部类,在第一次使用时才会初始化内部类Singleton,创建实例,保证了只有一个实例,并实现了延迟加载,加上静态域是线程安全的,减少了synchronized的开销。
- 优点:线程安全,延迟加载,没有锁开销,效率高,推荐。
四、总结
这四种是比较常见的单例模式实现方式
- 懒汉式实现了延迟加载,效率较高,但是线程不安全,适合单线程的时候使用
- 饿汉式在初始化的时候创建对象,保证了线程安全,但是在没有使用到这个对象的时候就浪费了内存空间,可以在多线程使用
- 双检索整合了懒汉式和饿汉式的优点,是线程安全,又实现了延迟加载,可以在多线程使用
- 静态内部类方式也是拥有懒汉式和饿汉式的优点,线程安全,延迟加载,还减少了锁的开销,提高了效率,推荐使用这种方式