单例模式

1.单例模式是什么?

    单例模式,顾名思义就是在整个运行域,一个类只有一个实例对象.

1.1为什么需要单例模式

    因为有的类型的实例对象的创建和销毁对资源来说消耗不大,但也有一些类型比较庞大和复杂,如果不断地去创建和销毁对象,且如果这些对象可以被复用,就会造成不必要的浪费.
在这里插入图片描述

2.如何实现单例模式?

要考虑的问题:
(1)是否线程安全
(2)是否懒加载
(3)是否可以反射破坏

2.1.懒汉模式

public class Singleton{
    //创建一个私有构造,外部不能去创建
    private Singleton(){};
    //初始化对象为null,不创建实例
    private static Singleton singleton = null;
    //获取通过下面这个函数
    public static Singleton getInstance(){
        //如果这个对象是null,则创建实例
        if(singleton==null){
            singleton = new Singleton();
        }
        //最终返回这个对象
        return singleton;
    }

    实例对象是第一次调用的时候才去创建,而不是饿汉单例那样程序一开始就自动创建和实例一个对象.懒汉模式,是当我需要的的时候我再去创建.
    如果遇见对象构建开销是比较大的,如果项目刚一开始就被构建,但是之后就根本没用过,那就很浪费资源.所以用这种用时随取.
  举个栗子:
    [饿汉]:饿汉面对许多好吃的,饿的难受了,管他是好的坏的,管他有没有用,我先拿过来吃了再说.也就是说不管有用没用,先占着.
    [懒汉]:天天就在床上躺着什么都不干,到了饭点了,才下床吃饭.需要的时候我再去,不需要的时候不动.

2.2.饿汉模式

    上面也提到了饿汉与懒汉的区别.饿汉模式就是在一开始就创建对象实例.差别不是很大.

public class Singleton{
    //创建一个私有构造,外部不能去创建
    private Singleton(){};
    //初始化对象为null,不创建实例
    private static Singleton singleton = new Singleton();
    //获取通过下面这个函数
    public static Singleton getInstance(){
        //最终返回这个对象
        return singleton;
    }
}

2.3.双检索模式

  但是哦.上面所描述的懒汉模式是线程不安全的.当我们执行if(singleton==null)的时候,如果有多个不同的线程进入,就会被实例化多次.
  如果不想让多个线程同时进去,可以加一个synchronized.public static synchronized Singleton getInstance(){.synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区.可以让线程互斥的访问代码,一次只能进入一个线程.
  但是我们的目的是…只想在对象创建的时候让线程同步,目前这样是在每次获取对象时线程同步,这样无疑解决点没找对.

public class Singleton{
    //创建一个私有构造,外部不能去创建
    private Singleton(){};
    //初始化对象为null,不创建实例
    private static Singleton singleton ;
    //我们让线程都进来
    public static Singleton getInstance(){
        //如果这个对象是null,则创建实例
        if(singleton==null){
            //如果实例对象没有创建,多个线程可以开始争抢对象锁
            //抢到锁的线程开始创建实例对象
            //之后的所有的线程执行到此处不用再创建,可以直接返回实例对象进行调用
            synchronized (Singleton.class){
                singleton = new Singleton();
            }
        }
        //最终返回这个对象
        return singleton;
    }
}

  但是还有这样一个情况存在,虽然在最后只有一个线程获取锁,但是其他线程在进入代码块后在if判断句处等待.抢到锁的线程执行完归还锁后,等待线程抢到锁又会创建一个实例对象.对象被多次创建了.

public class Singleton{
    //创建一个私有构造,外部不能去创建
    private Singleton(){};
    //初始化对象为null,不创建实例
    private static Singleton singleton ;
    //我们让线程都进来
    public static Singleton getInstance(){
        //如果这个对象是null,则创建实例
        if(singleton==null){
            //如果实例对象没有创建,多个线程可以开始争抢对象锁
            //抢到锁的线程开始创建实例对象
            //之后的所有的线程执行到此处不用再创建,可以直接返回实例对象进行调用
            synchronized (Singleton.class){
                if(singleton==null){
                    singleton = new Singleton();
                }
            }
        }
        //最终返回这个对象
        return singleton;
    }
}

  后续的某个等待线程抢到锁后,也会去判断实例对象是否已经创建,如果创建则不会再去创建

singleton = new Singleton().在指令层面这不是一个原子操作.
singleton = new Singleton()三段论:
(1)分配内存
(2)初始化对象
(3)对象指向内存地址
但是在真正执行的时候.Jvm虚拟机为了效率可能会对上面的指令进行重排,A线程执行完(1)直接执行(3).但是此时 singleton 还未被初始化…当下一个线程B执行到 if(singleton==null)时,返回false.直接跳过 singleton = new Singleton();返回 singleton对象,但是此时A线程还没有初始化完成,最后返回为null.

private volatile static  Singleton singleton ;

加上volatile可以阻止作用在singleton 上的指令重排问题.
双检索模式就是if判断句里嵌套一个if判断句.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值