设计模式 | 挑战单例模式(二)

628 阅读2分钟

这是我参与8月更文挑战的第25天,活动详情查看:8月更文挑战

内容接着上篇文章《挑战单例模式(一)》,上篇文章讲到了饿汉式的单例实现方法,这篇文章来讲懒汉式单例实现方式。

三、懒汉式

懒汉式单例在未使用使用之前是为实例化,只有调用取得实例的方法如getInstance时才会实例化对象,即在全局访问方法中创建实例。

实现方式如下:

public class SingletonInLazy {
​
    private SingletonInLazy(){ }
​
    public static SingletonInLazy INSTANCE = null;
​
    public static SingletonInLazy getInstance(){
        if(INSTANCE == null){
            INSTANCE = new SingletonInLazy();
        }
        return INSTANCE;
    }
}

类的实例化发生在提供的公共的静态方法getInstance。在单线程环境下运行,是任何问题的,但是在多线程环境是会实例化出多个对象。我们编写一个测试用例,开辟两个新的线程先后去执行getInstance方法。

  static final Runnable RUNNABLE = ()->{
        final SingletonInLazy instance = SingletonInLazy.getInstance();
        System.out.println(Thread.currentThread().getName()+ ":" + instance);
    };
​
    public static void main(String[] args) {
        final Thread thread1 = new Thread(RUNNABLE);
        final Thread thread2 = new Thread(RUNNABLE);
        thread1.start();
        thread2.start();
        System.out.println("end");
​
    }

运行结果如下:

懒汉式结果.png

结果相当明显,确实是生成了两个实例对象。但这种情况也不一定是肯定会发生的,有的时候也只会产生一个对象,所以意味中懒汉式的单例存在线程安全的隐患。

小提示,如果想在多线程模式下进行调试代码,可以使用Idea工具的线程模式进行调试,手动控制执行顺序来跟踪内存的变化状态。

既然线程不安全,我们可以通过加锁的方式保证线程安全,比如给getInstance静态方法加上synchronized关键字,使这个方法变成线程安全的。

 public synchronized static SingletonInAnotherHungry getInstance(){
        return INSTANCE;
    }

再多线程下环境下调用getInstance方法,再也没有出现过多个对象,线程安全的问题解决了。但是,使用加锁的方式,在开辟的线程数量比较多的情况下,·】synchronized升级成重量级锁,未获取到锁资源的线程会处于阻塞的状态,从而导致资源非必要的消耗,程序的运行性能大幅下降。那么有没有更好的方式呢?当然,答案就是使用双重检查锁


没看够,请跳转到下一篇文章设计模式 | 挑战单例模式(三)