单例模式个人总结(饿汉式、懒汉式)

前言

在别人的面经,看到手写单例模式,勾起了遥远的记录。来复习总结一下。

单例模式涉及概念

  1. 目的:众所周知,我们通过new都能创建一个实例,但在一些程序运行中,只允许存在一个对象实例,那么我们就需要从代码层面保证只有一个对象实例
  2. 运用场景:
    1. 一些类并不是轻量级的,一个项目中存在一个就行,譬如一些数据源连接对象
  1. 实现方式
    1. 目的:有很多方法都能实现这种方式,但本质都一样,都是从代码层面保证只产生一个对象
  1. 我们需要再明白饿汉式和懒汉式
    1. 饿汉式:在一开始,类装载的时候,实例就创建出来。缺点就是,不知道是不是需要用到就创建
    2. 懒汉式:需要用到实例的时候再创建出来
      1. 线程安全问题:创建的方式需要注意,不然会导致创建出来不止一个,则不是单例模式。

实现代码

饿汉式

  1. 通过静态常量的方式创建对象
public class Car1 {
    // 1.将构造器私有化,外部不能new
    private Car1(){
    }
    // 2.本类内部创建对象实例
    private final static Car1 car = new Car1();
    // 3.对外提供一个共有的静态方法,返回实例对象
    public static Car1 getCar(){
        return car;
    }
}
  1. 通过静态代码块的方式创建对象
public class Car2 {
    private Car2(){
    }
    private final static Car2 car;

    static {
        car = new Car2();
    }
    public static Car2 getCar(){
        return car;
    }
}
  1. 区别:这两种方法都是私有化构造方法,但是在类装载的时候就已经创建好实例,仅通过暴露一个方法,供外部获取。

懒汉式

  1. 线程不安全(跟Redis缓存击穿有点类似)
public class Car3 {
    //1.私有化构造方法
    private Car3(){}

    private static Car3 car;
    //2. getCar,如果没有就创建,如果有就返回
    public static Car3 getCar(){
        if(car == null ){
            //缺点:如果是单线程,这个代码并没有问题,但多线程情况下,在代码执行到这里,多个线程都判断null,就都会去创建实例,这就破坏了单例模式
            car = new Car3();
        }
        return car;
    }
}
  1. 线程安全(加锁)
public class Car4 {
    //1.私有化构造方法
    private Car4(){}

    private static Car4 car;
    //2. getCar,如果没有就创建,如果有就返回.通过加锁保证只有一个线程去创建
    public synchronized static Car4 getCar(){
        if(car == null ){
            car = new Car4();
        }
        return car;
    }
}
  1. 双重检查
    1. 目的:我们并不想在方法上直接解锁的方式去解决问题,即便问题解决,但不是最优解
    2. 为什么是双重检查?在执行一个if是null的时候,进行同步代码块的时候,有可能是多个线程在等待,可能前面的线程已经创建了,后续等待的线程就不需要去创建
public class Car5 {
    private Car5(){}

    private static Car5 car;

    public Car5 getCar(){
        if(car == null){
            synchronized (Car5.class){
                if(car == null){
                    car = new Car5();
                }
            }
        }
        return car;
    }
}
  1. 静态内部类
    1. 利用JVM提供的静态内部类的方法是实现,JVM底层会帮助我们,加载Car的时候,car实例并不会被创建,而调用getCar的时候,需要CarInstance,将CarInstance这个类加载并创建Car出来。利用类装载的机制保证
public class Car6 {
    private static Car6 instance;
    private Car6(){}
    private static class CarInstance{
        private static final Car6 INSTANCE = new Car6();
    }
    public static Car6 getCar(){
        return CarInstance.INSTANCE;
    }
}

总结

曾经看见过12种实现单例模式,我觉得我是无缘了,脑子记不得那么多种。(上面也只有几种常见的。只要满足在程序运行过程中,什么情况都只有一个实例就行。实现方式可以很多种)

但无论是饿汉还是懒汉本质都是通过只创建一个实例来完成。而饿汉式应用场景也很多。因为很多对象本身就轻量,所以饿汉式用得地方很多。如果我们使用懒汉式则考虑线程安全问题。

至于可以通过反射和序列化可以破坏单例模式,可以通过枚举和不实现序列化接口进行实现。反射会判断是不是枚举,如果是枚举会抛出异常,所以枚举是不会被破坏单例的。因此也可以使用枚举实现单例模式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值