Java单例模式几种写法

第一种:饿汉式

public class Singleton {

    private static Singleton instance = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return instance;
    }

}

这是实现一个安全的单例模式的最简单粗暴的写法,这种实现方式我们称之为饿汉式。之所以称之为饿汉式,是因为肚子很饿了,想马上吃到东西,不想等待生产时间。这种写法,在类被加载的时候就把Singleton实例给创建出来了。

饿汉式的缺点就是,可能在还不需要此实例的时候就已经把实例创建出来了,没起到lazy loading的效果。优点就是实现简单,而且安全可靠。

第二种:双重检查模式

public class Singleton {
    
    private static volatile Singleton singleton;

    private Singleton() {
    }

    public static Singleton getInstance() {

        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;

    }

}

推荐理由:

  1. 延迟初始化。和懒汉模式一致,只有在初次调用静态方法getSingleton,才会初始化signleton实例。
  2. 性能优化。同步会造成性能下降,在同步前通过判读singleton是否初始化,减少不必要的同步开销。
  3. 线程安全。同步创建Singleton对象,同时注意到静态变量singleton使用volatile修饰。

为什么要使用volatile修饰?

虽然已经使用synchronized进行同步,但是在创建对象时,会有下面的伪代码:

memory=allocate(); //1:分配内存空间
ctorInstance(); //2:初始化对象
singleton=memory; //3:设置singleton指向刚排序的内存空间

当线程A在执行上面伪代码时,2和3可能会发生重排序,因为重排序并不影响运行结果,还可以提升性能,所以JVM是允许的。如果此时伪代码发生重排序,步骤变为1->3->2,线程A执行到第3步时,线程B调用getsingleton方法,在判断singleton==null时不为null,则返回singleton。但此时singleton并还没初始化完毕,线程B访问的将是个还没初始化完毕的对象。当声明对象的引用为volatile后,伪代码的2、3的重排序在多线程中将被禁止!

第三种:静态内部类模式

public class Singleton {

    private static class SingletonInner {
        private static final Singleton singleton = new Singleton();
    }
    
    private Singleton() {
    }

    public static Singleton getInstance() {
        return SingletonInner.singleton;
    }
}

推荐理由:

  1. 实现代码简洁。和双重检查单例对比,静态内部类单例实现代码简洁明了。
  2. 延迟初始化。静态内部类不会在Singleton类加载时就加载,而是在调用getInstance()方法时才进行加载,达到了懒加载的效果
  3. 线程安全。JVM在执行类的初始化阶段,通过加锁来确保类的 < clinit > 方法仅被执行一次,所以只会实例化一个Singleton对象

第四种:枚举模式

以上几种单例模式,序列化和反序列化以及反射对其都是有破坏的。采用枚举则不会

public enum Singleton {

    INSTANCE;

    public void doSomething() {
        System.out.println("doSomething");
    }

}

直接通过Singleton.INSTANCE.doSomething()的方式调用即可。方便、简洁又安全。

参考链接:

https://www.jb51.net/article/162539.htm

https://www.cnblogs.com/happy4java/p/11206105.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值