JAVA单例模式实现详解

单例模式的特点

  • 类的内部包括待返回的类的实例,为private static类型
  • 类的构造函数为私有构造函数,以防止在其他类中实例化,private的作用是防止在其他类中用构造函数创建该类的实例
  • 提供一个获取实例的静态方法

单例模式1:饱汉模式+考虑了线程安全(双重检查锁)

public class Singleton1 {
    private static volatile Singleton1 instance = null;//volatile不具有原子性

    //构造方法:如果构造方法是 public,那么任何类都可以 new 我的对象,不能保证单例,
    // 所以单例模式构造方法一定是 private的
    private Singleton1() {

    }
    public static Singleton1 getInstance() {

        if (instance == null) {//只有instance == null这种情况下需要进行加锁synchronized
            synchronized (Singleton1.class) {//静态方法中上锁用的是类名.class
                if (instance == null)
                    instance = new Singleton1();
            }
        }
        return instance;

    }
}

为什么需要第一重instance == null呢?

这里就涉及一个性能问题了,因为对于单例模式的话,newSingleTon()只需要执行一次就 OK 了,

而如果没有第一重instance == null 的话,每一次有线程进入getInstance()时,均会执行锁定操作来实现线程同步,

这是非常耗费性能的,而如果我加上第一重instance == null 的话,

那么就只有在第一次,也就是instance ==null 成立时的情况下执行一次锁定以实现线程同步,

而以后的话,便只要直接返回Singleton实例就 OK 了而根本无需再进入 lock语句块了,这样就可以解决由线程同步带来的性能问题了。

为什么需要第二重instance == null呢?

考虑这样一种情况,就是有两个线程同时到达,即同时调用getInstance() 方法,

此时由于instance == null ,所以很明显,两个线程都可以通过第一重的 instance == null ,

进入第一重 if语句后,由于存在锁机制,所以会有一个线程进入 lock 语句并进入第二重 instance == null ,

而另外的一个线程则会在lock 语句的外面等待。

而当第一个线程执行完new SingleTon()语句后,便会退出锁定区域,此时,第二个线程便可以进入lock 语句块,

此时,如果没有第二重instance == null 的话,那么第二个线程还是可以调用 new SingleTon()语句,

这样第二个线程也会创建一个SingleTon实例,这样也还是违背了单例模式的初衷的,

所以这里必须要使用双重检查锁定。

为什么要用volatile?

主要是为了保证有序性(禁止指令重排序)

java中new一个对象是由多个步骤组成的,而这些步骤可能会被编译器重排序,从而导致其他线程获取到一个没有被完全初始化的对象,而 volatile 就
是告诉编译器不要重排序。

这里volatile的作用仅仅是阻止指令重排序, 不涉及可见性问题, 可见性已经由synchronized来保证了(synchronized也有阻止重排序的功能, 但是其由monitor来实现, monitorenter和monitorexit之间的指令仍可能被重排序).

单例模式2:饿汉模式

public class Singleton2 {
    private static final Singleton2 singleton = new Singleton2();
    private Singleton2() {
    }
    public static Singleton2 getInstance() {
        return singleton;
    }
}
  • 饿汉的好处是天生的线程安全(得益于类加载机制),写起来超级简单,使用时没有延迟;
  • 坏处是有可能造成资源浪费(如果类加载后就一直不使用单例的话)

单例模式3:Holder模式(增加了一个静态内部类)

public class Singleton3 {
    // 加载一个类时,其内部类不会同时被加载。
    // 一个内部类被加载,当且仅当其某个静态成员(静态域、构造器、静态方法等)被调用时发生。
    private static class SingletonHolder {
        private static final Singleton3 singleton = new Singleton3();
        private SingletonHolder() {
        }
    }
    private Singleton3() {
    }
    public static Singleton3 getInstance() {
        return SingletonHolder.singleton;
    }
}
  • 相对于饿汉模式,Holder模式仅增加了一个静态内部类的成本,
  • 与饱汉的线程安全写法的效果相当(略优),都是比较受欢迎的实现方式。同样建议考虑。
  • 该实现方式比较简单,而且既实现了由前述事实所保证的惰性初始化(Lazy-Initialazation),又由JVM保证了多线程并发访问的正确性。

参考链接:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值