java多线程——单例模式

目录

1.概念

2.实现


1.概念

1.1 什么是单例模式

单例模式能保证某个类在程序中只存在唯一一份实例, 而不会创建出多个实例.

这一点在很多场景上都需要。比如 JDBC 中的 DataSource 实例就只需要一个

1.2 单例模式分类 

单例模式具体的实现方式, 分成 "饿汉" 和 "懒汉" 两种.

所谓”饿汉“模式,就是指程序启动则立刻创建实例,可以形象理解为”饿汉“迫不及待想要吃饭(创建实例)。但不管是否使用都创建对象可能会浪费内存,但是线程安全。

而”懒汉“模式,则是程序启动时不着急创建实例,等到真正使用时再创建,就好像”懒汉“只有在一件事真正需要被做时他才去做,绝对不可能提前。减少了资源浪费,线程不安全,但可以通过加锁来实现线程安全。

2.实现 

2.1 饿汉模式

class Singleton{
    private static Singleton instance=new Singleton();//静态成员表示实例
    private Singleton(){};//构造方法私有化
    public static Singleton getInstance(){
      return instance;
    };
}

这里我们通过静态成员表示实例(唯一性)+构造方法私有化(堵住new创建新实例的口子)从而实现了单例模式。当Singleton类被加载时就会执行此处实例化操作,突出了”饿汉“的特点,非常急迫。那么段代码在多线程的情况下会存在线程安全问题吗?答案是不会,因为类加载的方式是按需加载,且只加载一次

2.2 懒汉模式-单线程版

class Singleton{
    private static Singleton instance=null();//不立刻创建实例
    private Singleton(){};//私有化构造方法
    public static Singleton getInstance(){
        if(instance==null){
            instance=new Singleton();
        }
        return instance;
    }
}

这个单例模式的代码放在单线程情况下是完全没有问题的,但是假如在多线程的状态下

 线程安全问题发生在首次创建实例时. 如果在多个线程中同时调用 getInstance 方法, 就可能导致 创建出多个实例.

一旦实例已经创建好了, 后面再多线程环境调用 getInstance 就不再有线程安全问题了(不再修改 instance 了)

所以我们需要进行加锁操作来解决这个问题。

 2.3 懒汉模式-多线程版

class Singleton{
    private static Singleton instance=null;//不立刻创建实例
    private Singleton(){};//私有化构造方法
    public synchronized static Singleton getInstance(){
        if(instance==null){
            instance=new Singleton();
        }
        return instance;
    }
}

2.4 懒汉模式-多线程版(改进)

我们仔细观察上面的代码,可以发现一些改进的空间。加锁 / 解锁是一件开销比较高的事情. 而懒汉模式的线程不安全只是发生在首次创建实例的时候. 因此后续使用的时候, 不必再进行加锁了。其次 由于可能有多个线程来尝试获取我们的实例,jvm可能会对此进行优化产生内存可见性问题(只是可能),所以我们保守起见还是应该加上volatile关键字

于是我们得到以下改进方案:

1.加锁

2.双重if判断

3.volatile 

class Singleton{
    private static Singleton instance=null;//不立刻创建实例
    private Singleton(){};//私有化构造方法
    public static Singleton getInstance(){
        if(instance==null){
            synchronized (Singleton.class){//类名.class返回的是一个class对象
                if(instance==null){
                    instance=new Singleton();
                }
            }
        }
        return instance;
    }
}

这里我们需要着重理解一下双重if的意思,最外层的if是判断当前前是否已经把 instance 实例创建出来了。如果已经创建就不需要加锁了。当多线程首次调用 getInstance, 大家可能都发现 instance 为 null, 于是又继续往下执行来竞争锁, 其中竞争成功的线程, 再完成创建实例的操作。当这个实例创建完成后,其他竞争的线程就被里层的if挡住了,不会继续创建其他实例。最后就是加上volatile修饰,防止出现内存可见性问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值