单例模式

单例模式是23种设计模式中最简单的之一,本文将从原理是讲解五种线程安全的单例模式写法
单例模式类图

1.饿汉式

特点:在jvm加载类时,就会初始化静态属性instance,因此是线程安全的
缺陷:这种方式不是一种懒加载方式,即使并没有调用getInstance方法也会实例化对象;另外如果单例对象的创建需要依赖外部资源时,这种方式也就不适用。

public class Singleton{

    public static final Singleton instance = new Singleton();

    private Singleton(){};

    public static Singleton getInstance(){
        return instance;
    }
}

2.懒汉式

特点:通过使用synchronized来保证线程安全
缺陷:synchronized修饰的方法执行效率低,任何时刻都只能有一个线程能调用getInstance()方法,并且同步操作也只有在第一次创建单例对象的时候才需要

public class Singleton{

    public static Singleton instance;

    private Singleton(){};

    public static synchronized Singleton getInstance(){
        if(instance == null)
            instance = new Singleton();
        return instance;
    }
}

3.双检锁

特点:在synchronized同步块里面和外面分别做一次空判断,防止多个线程通过第一层判断,并且只有第一次创建单例对象才会进入同步块,相对于懒汉式效率高

public class Singleton{

    public static volatile Singleton instance;

    private Singleton(){};

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

问题:为什么要用volatile修饰instance?
instance = new Singleton()这一行并不是一个原子操作,是以下三个步骤组成:
1.给instance对象分配内存空间
2.调用Singleton的构造方法初始化对象
3.将instance对象指向分配的内存空间
执行完第三步,instance对象就不为null了,如果它没有被volatile修饰,编译器在编译时会进行指令重排序优化,无法保证2、3步的执行顺序,可能是1-2-3或1-3-2,如果是按照1-3-2的顺序执行,则有可能会执行了1-3,另一个线程在调用getInstance()时,看到的是一个非空但未构造完成的instance,如果此时使用就会报错。
使用volatile修饰instance可以禁止指令重排序优化,在对volatile赋值操作之后会有一个内存屏障 lock add $0x0,(%esp) ;它保证了对volatile变量的写操作,都先行发生于后面对这个变量的读操作,”后面”是指时间上的先后顺序,在以上例子在,其他线程对instance的读取操作必然发生在1-2-3或1-3-2执行完成之后,从而不会发生读取到一个”创建了一半”的对象。

4. 静态内部类

特点:依赖jvm类加载机制来实现线程安全,但是由于SingletonHolder类是私有的,在调用getInstance()之前不会被初始化(反射除外),因此它是一种懒汉式,同时没有线程同步的效率问题,一般推荐采用这种写法。

public class Singleton{

    private Singleton(){};

    public static final Singleton getInstance(){
        return SingletonHolder.INSTANCE;
    }

    private static class SingletonHolder{
        private static final Singleton INSTANCE = new Singleton();
    }
}

5.枚举

特点:单元素的枚举实现单例,无偿的提供了序列化,绝对防止多次实例化,即使是面对复杂的序列化和反射攻击时。“单元素的枚举类型已成为实现单例的最佳方法”(摘自《Effective java 》)

public enum Singleton{
    INSTANCE;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值