单例模式实现

Posted by New Boy on February 14, 2018

前言

说起单例模式,应该设计模式中算是代码比较短,其主要作用是保证一个类只有一个实例,节省内存空间,也避免了频繁的创建与销毁对象,比如在spring中bean的创建时,就默认采取了单例模式。单例模式主要包括懒汉式和饿汉式。顾名思义,懒汉式就是“懒”,在你需要的时候我再给你创建对象,而对于饿汉式,则在于“饿”,在类加载时就就生成实例对象。

实现

为了不让外部类创建对象,一般是通过对构造函数的私有化来实现,

懒汉式

public class lazy {
    private static volatile lazy instance;

    private lazy() {

    }

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

在学习懒汉式的时候还是遇到一些问题的

关于双重检查锁的问题

以正常的思维,我们开始去写newInstance很容易想成是这样的:

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

不过如果在这种情况下,就会出现这么一种情况,在大量线程同时调用newInstance()时,就会出现很严重的锁竞争的问题,而改进的办法,就是在同步代码块外部再添加一个null的判断,那么在实例创建好后,之后进来的线程就可以直接返回此实例。

关于volatile变量的问题

在性能上得到优化后,不过在线程安全上还是存在问题。场景是这样的。现在有个线程A和线程B,同时到达synchronized(lazy.class)处,A抢到了锁,执行完后,B获取到了锁,此时若线程A没有将工作内存中的值写回到主内存中,那么此时线程B也创建了一个对象,也就不符合我们所说的“一个类对应一个实例”。这时我们就需要给实例添加一个volatile修饰,使用它的可见性去解决这个问题。说到volatile,还有很多的东西需要去解释,可以看这篇文章。

饿汉式

饿汉式相对较简单

public class hungry {
    private static hungry instance = new hungry();

    private hungry() { }

    public static hungry newInstance() {
        return instance;
    }
}