java单例类的几种实现

Posted fuzhepan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java单例类的几种实现相关的知识,希望对你有一定的参考价值。

一,最简单的方式

public class Singleton{
    private Singleton(){};
    private static Singleton instance = new Singleton();
    public static Singleton getInstance(){
        return instance;
    }
}

首先构造函数声明为private,防止被外部创建该类的实例。声明一个static的成员变量instance并分配实例,当Singleton类被加载时,instance便会被创建,可以通过静态方法getInstance方法获取到该实例。
优点是实现简单,且没有线程安全问题。缺点是Singleton被引用时instance实例就已经创建,即使实例并没有用到。

二,懒加载的单例类

public class Singleton{
    private Singleton(){};
    private static Singleton instance = null;
    public static synchronized Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

因为instance初始值为null,所以在Singleton被加载的时候,并不会实例化,只有调用getInstance方法的时候才会创建实例。为了防止多线程并发调用getInstance方法时instance被多次创建,所以使用synchronized关键字进行线程同步。
该实现缺点是,每次调用getInstance方法都要进行线程同步,影响并发量。

三,改进懒加载的单例类

public class Singleton{
    private Singleton(){};
    private static volatile Singleton instance = null;
    public static Singleton getInstance(){
        if(instance == null){
            synchronized(Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

通过双检索的方式进行优化,只有在intance没有值时才进行线程同步,此后会直接返回实例。需要注意的是,在synchronized代码块中需要再判断一次instance是否为null,防止多个线程同时通过了第一个为null的判断。
还需要注意的是,instance需要volatile修饰,防止指令重排序导致的错误。
volatile除了保证线程缓存及时同步到主内存并清理其他线程缓存的值,还有一个作用就是防止指令重排序。 instance = new Singleton() 这行代码编译后会拆分成三个指令,可以理解成如下代码:
1,Singleton temp = malloc(); // 分配内存
2,constructor(temp); // 调用构造函数对分配的内存进行初始化
3,instance = temp; // 初始化完成的内存地址赋值给instance
编译器为了优化指令,重排序后,可能会变成了下面的代码:
1,Singleton temp = malloc(); // 分配内存
2,instance = temp; // 初始化完成的内存地址赋值给instance
3,constructor(instance); // 调用构造函数对分配的内存进行初始化
如果代码执行了上面的第二步,instance已经赋值不为null,但并没有初始化,这是如果第二个线程调用getInstance方法就会直接获得instance,调用instance时引发错误。volatile可以防止重排序。

四,通过内部类实现单例

public class Singleton{
    private static class SingletonHolder{
        private static Singleton instance = new Singleton();
    }

    private Singleton(){};
    private static volatile Singleton instance = null;
    public static Singleton getInstance(){
        return SingletonHolder.instance;
    }
}

Singleton的实例被静态内部类SingletonHolder持有,只有在调用getInstance方法时,SingletonHolder才会被加载,instance被实例化。既实现懒加载,也不会有线程安全问题。

 

以上是关于java单例类的几种实现的主要内容,如果未能解决你的问题,请参考以下文章

单例模式的几种写法的差异

双重检查锁实现单例(java)

如何实现单例模式

java单例模式的几种实现

java设计模式之单例模式

Kotlin中数据类和单例类的实现和讲解面向对象编程接口的实现