【单例深思】静态内部类实现详解

来源:互联网 发布:宁弈和知微的结局 编辑:程序博客网 时间:2024/06/06 17:26
静态内部类实现是我个人比较推荐的,其实现如下:

public class Singleton {
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton() {}
    public static final Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
 
上面实现中,通过声明一个静态内部类SingletonHolder 来持有单例 INSTANCE ,在getInstance()方法中返回该实例。

我们知道,静态内部类与非静态内部类之间有很大的区别,非静态内部类在编译完成之后会隐含地保存着一个指向创建它的外围类的引用,但是静态内部类却没有。这意味着静态内部类的创建是不需要依赖于外围类,它不能使用任何外围类的非static成员变量和方法,某种程度上来说,静态内部类可以当成是一个独立的类。它的初始化和外部类初始化无关。

下面详细介绍一下它是如何解决单例中的各种问题的。

首先,它是如何实现延迟加载的?

由于INSTANCE 在静态内部类中声明并实例化,在【单例深思】饿汉式与类加载 中我们知道有且只有5种情况会引发初始化操作,初始化阶段是执行类构造器<clinit>() 方法的过程。<clinit>() 方法是由编译器自动收集类中的所有类变量(static的赋值动作和静态语句块static{})块中的语句合并产生的。因为外部类初始化不会引发静态内部类SingletonHolder的初始化,所以只有真正调用getInstance()方法,执行 SingletonHolder.INSTANCE 时,才会引发静态内部类初始化,从而完成实例化。所以,静态内部类实现是延迟加载的。


它如何保证实例化时线程安全?

既然静态内部类初始化可以当成为一个普通类的初始化,根据【单例深思】饿汉式与类加载 中所描述的类加载过程,可以知道虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确的加锁、同步。所以,静态内部类实现是在实例化时是线程安全

在其他方面,由于在读取实例的时候不会进行同步,所以没有性能缺陷;没有使用 volatile ,也不依赖 JDK 版本。

总之,静态内部类实现单例由于编写简单,满足线程安全和延迟加载,无其他明显缺点,是一种在工作中用的比较多的方式。

1 0