This page looks best with JavaScript enabled

Java 单例实现

 ·  ☕ 2 min read

单例常见实现方式

  • 饿汉式:在类被加载时就初始化单例
  • 懒汉式:在需要的地方才初始化单例

饿汉式加载问题

传统的在类加载时就创建单例的方法存在一些问题:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class SingleInstance {

    private SingleInstance() {...}
    
    private static SingleInstance mInstance = new SingleInstance();
    
    public static SingleInstance getInstance(){
        return mInstance;
    }
}

如果类的实例化含义太多的操作,就可能会影响程序的性能,启动时间等。而且如果单例在程序中并未用到,则会一直存在一个无法被GC回收的对象,造成浪费。

如果单例一定会使用,而且构造方法中又没有太多操作,则可以在类加载时就初始化单例。

懒汉式加载问题

下面这种懒加载,在多线程情况下可能会造成创建了多个单例。
线程A正在执行new操作,线程B同时来获取单例,此时instance还是null,线程B就也会去执行new操作。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class SingleInstance {

    private SingleInstance() {}

    private static SingleInstance mInstance;
    
    public static SingleInstance getmInstance() {
        if (null == mInstance) {
            mInstance = new SingleInstance();
        }
        return mInstance;
    }
}

使用synchronized优化懒加载

  • 方式一:对获取方法加锁(不推荐)
1
2
3
4
5
6
public synchronized static SingleInstance getInstance(){
    if(null==mInstance){
        mInstance = new SingleInstance();
    }
    return mInstance;
}

这种方式加锁,会造成性能问题。synchronized修饰了方法,所有线程获取单例时都会经历同步锁,不论单例是否已经被创建。而且获取单例可能是个频繁的操作,直接对方法加锁会降低性能。

  • 方式二:双重验证,只对构建加锁
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
private volatile static SingleInstance mInstance;

public static SingleInstance getInstance() {
    // 避免在 mInstance 初始化之后, 多个线程每次都获取同步锁
    if (null == mInstance) {
        synchronized (SingleInstance.class) {
            // JVM 可能存在指令重排序, 先为对象分配内存并赋值给引用返回, 后进行初始化
            if(null == mInstance) {
                mInstance = new SingleInstance();
            }
        }
    }
    return mInstance;
}

推荐这种方式实现单例懒加载,只当单例没有被构造时才会进入加锁的部分。而且使用volatitle关键字, 禁止对 volatile 修饰的变量进行指令重排序, 且 volatitle 会让每个线程每次都从主内存获取最新的变量值, 确保了变量在多线程下的可见性.

使用静态内部类优化懒加载

静态内部类(Holder)只有在被用到时才会被加载, 加载过程由 JVM 保证多线程安全, 而且静态变量的赋值只会在类加载时执行一次. 后续在获取单例时因为内部类(Holder)已经加载过了所以不会再走赋值过程, 直接返回静态变量(mInstance).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class SingleInstance {

    private SingleInstance() {}

    public static SingleInstance getInstance() {
        return Holder.mInstance;
    }

    private static class Holder {
        private static final SingleInstance mInstance = new SingleInstance();
    }

}
Support the author with
alipay QR Code
wechat QR Code

Yang
WRITTEN BY
Yang
Developer