实现满足Java以下要求的Singleton的最佳方法是什么?

时间:2019-01-07 08:28:25

标签: java android multithreading thread-safety singleton

在我们的android应用中,我们具有以下要求

  1. 预加载我们使用的所有字体
  2. 缓存已加载的字体

到目前为止,我们有一个简单的单例,看起来像这样:

@NotThreadSafe
public static void init(Context context) { 

 if (instance == null) {
            instance = new TypefaceHelper(context);
            instance.setAssetManager(context.getAssets());
            instance.setAllTypefaces();
  }
}

以下是我遇到的问题:

  • 它不是线程安全的。请注意,到目前为止,它始终在Main-Thread中访问(但来自不同的类)
  • setAllTypefaces()耗时超过50毫秒 平均加载我们正在使用的4种字体。

因此,每次调用MyApplication(扩展了Android应用程序的扩展)时,我们的字体初始化本身都需要50毫秒以上的时间。

作为性能调整的一部分,我们认为在后台加载字体是有意义的。因此,现在我想在后台加载字体,并确保初始化线程安全。

这是我想出的设计

public class TypefaceHelper {

   private static TypefaceHelper instance;

   private volatile Typeface proximaRegular;

   private final Object typefaceLock = new Object();

   private static final Object constructorLock = new Object();



@GuardedBy("constructorLock")
public static void init(Context context) {

    synchronized (constructorLock) {

        if (instance == null) {
            instance = new TypefaceHelper(context);
            instance.setAssetManager(context.getAssets());
            MyApplicationHelper.getInstance().getExecutorService().execute(new Runnable() {
                @Override
                public void run() {
                    instance.setAllTypefaces();  // Set up all Typefaces in background thread
                }
            });

        }
    }

}


@Nullable 
public static TypefaceHelper getInstance() { // Existing code used in 100+ places. Not touched now.
    return instance;
}

// Accessing a typeface. Use Double-checked locking to ensure it works 
public Typeface getProximaRegular() {
    if(proximaRegular==null){ // The variable must be volatile to prevent compiler re-orderings and optimizations
        synchronized (typefaceLock){
            if(proximaRegular==null){
                proximaRegular = Typeface.createFromAsset(assetManager, "fonts/ProximaNova-Regular.otf");
            }
        }
    }
    return proximaRegular;
}


@GuardedBy("typefaceLock")
private void setAllTypefaces(){


    synchronized (typefaceLock) {


        proximaRegular = Typeface.createFromAsset(assetManager, "fonts/ProximaNova-Regular.otf");

        // Initialize 3 other typefaces here 


    }


  }

}

请注意,我正在getProxmimaRegular()中的同步块外使用空检查,因为它是从100多个地方访问的,如果它不为空,我不希望获得锁。

我的问题是:

1)在后台加载单例的成员变量是否不好? (否则性能会受到很大影响)

2)在这里为init()使用单独的锁在这里过大了吗?

3)考虑到我的要求,我该如何使其更好?

0 个答案:

没有答案