单例模式--详细介绍--java篇

单例模式

单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例。(来自百度百科的客套话)

其实就是保证一个类创建对象时保证只创建一个实例,解决频繁创建销毁的问题,减少内存开销。

接下来直接来以代码形式讲述一下单例模式的几种创建方式

1、饿汉式

这也是最基础的实现方式

  1. 私有化构造函数
  2. 定义一个私有的对象在类加载时期new出来
  3. 提供一个公有方法获取对象

优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。

public class SingleEverboDemo1 {
    //创建 SingleEverboDemo1 的一个对象
    private static SingleEverboDemo1 instance = new SingleEverboDemo1();
    //让构造函数为 private,这样该类就不会被实例化
    private SingleEverboDemo1(){}
    //获取唯一可用的对象
    public static SingleEverboDemo1 getInstance(){
        return instance;
    }
}

2、懒汉式(线程不安全)

与饿汉式不同的是类加载期间没有初始化对象,而是什么时候需要用到对象什么时候创建,但是在没有加锁的情况下,不适用于多线程。

优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。

public class SingleEverboDemo2 {
    //创建 SingleEverboDemo2 的一个对象
    private static SingleEverboDemo2 instance;
    //让构造函数为 private,这样该类就不会被实例化
    private SingleEverboDemo2() {
    }
    //获取唯一可用的对象
    public static SingleEverboDemo2 getInstance() {
        if (instance == null) {
            instance = new SingleEverboDemo2();
        }
        return instance;
    }
}

3、懒汉式(线程安全)

与第二个相比仅仅加了一个synchronized关键字,保证了多个线程在创建对象时只有一个能创建成功。
这样也会因为锁粒度太大而导致效率太低的缺点

public class SingleEverboDemo3 {
    //创建 SingleEverboDemo3  的一个对象
    private static SingleEverboDemo3 instance;
    //让构造函数为 private,这样该类就不会被实例化
    private SingleEverboDemo3() {
    }
    //获取唯一可用的对象
    public static synchronized SingleEverboDemo3 getInstance() {
        if (instance == null) {
            instance = new SingleEverboDemo3();
        }
        return instance;
    }
}

4、双检锁/双重校验锁(DCL,即 double-checked locking)

这里与懒汉式(线程安全)相比,降低了锁粒度,假设有10000个线程同时获取对象,进入方法先进行判断对象是否为空,如果为空即获取锁并重新进行判断对象是否为空,如果为空即可创建对象。

第一次if (singleton == null)的判断原因如下:
如果不加判断,这10000个线程会同时去获取锁,锁竞争会很激烈以至于效率会变低,但加了判断之后如果有一个线程获取到锁之后,别的线程就不会进行锁竞争从而提升效率

public class SingleEverboDemo4 {
    //创建 SingleEverboDemo4 的一个对象
    private volatile static SingleEverboDemo4 singleton;
    //让构造函数为 private,这样该类就不会被实例化
    private SingleEverboDemo4 (){}

    public static SingleEverboDemo4 getSingleton() {
        if (singleton == null) {
            synchronized (SingleEverboDemo4.class) {
                if (singleton == null) {
                    singleton = new SingleEverboDemo4();
                }
            }
        }
        return singleton;
    }
}

与前面不同的是,这里的singleton对象在创建时,加了volatile关键字,原因如下:
1、程序可以乱序执行
2、new 一个对象时存在半初始化状态(未初始化完毕)
而new对象有三步

  1. 0 new #2 即定义 singleton=0 (申请一个内存默认值为0)
  2. invokespecial #3 <T.init> (构造方法执行)
  3. astore_1 即singleton = new SingleEverboDemo4();(建立关联)

当2和3顺序乱序之后,会出现一种情况。
第一个线程创建对象指令顺序为132,那么当它执行到第三步时,此时另外一个线程进来了,发现singleton!=null,于是返回了一个第一个线程未初始化完毕的对象,而这个对象由于没有执行构造方法而被赋值为默认值( 0 ),导致这个对象出错。

volatile刚好解决了这个问题,下面是volatile关键字的作用

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:

1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。

2)禁止进行指令重排序。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值