单例模式——应用最广的模式(二)

来源:互联网 发布:淘宝卖家订单打印 编辑:程序博客网 时间:2024/04/30 00:36
定义:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
核心原理:将构造函数私有化,并通过静态方法获取一个唯一的实例,在这个获取的过程中必须保证线程安全、防止反序列化导致重新生成实例对象等问题。

实现单例模式主要关键点:
(1)构造函数不对外开放,一般为private;
(2)通过一个静态方法或者枚举返回单例类对象;
(3)确保单例类的对象有且只有一个,尤其是在多线程的环境下(比较困难);
(4)确定单例类对象在反序列时不会重新构建对象。

饿汉模式和懒汉模式区别:建立单例对象的时间不同。

1、饿汉模式:

public class Singleton{    //声明静态对象时就已经初始化,不管是否用得上    private static Singleton singleton = new Singleton();    private Singleton(){}    public static Singleton getInstance(){        return singleton;    }}

饿汉模式:无法解决在多线程环境下,单例类的对象有且只有一个,即线程安全。

2、懒汉模式:(不建议用)

public class Singleton{    private static Singleton instance;    private Singleton(){}    //每次都同步    public static synchronized Singleton getInstance(){         if(instance == null){            //只有在使用时才会被实例化,在一定程度上节约了资源            instance = new Singleton();         }        return instance;    }}

懒汉模式
优点:单例只有在使用时才被实例化,在一定程度上节约了资源;
缺点:(1)第一次加载时需要及时进行实例化,反应稍慢;
           (2)最大是问题是每次调用getInstance都进行同步,造成不必要的同步开销。所以不建议使用。

3、双重检查锁定 :Double Check Lock (DCL) (可满足大部分需求)

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


优点:(1)需要时才初始化单例;(2)保证线程安全,且单例对象初始化后调用getInstance不进行同步锁。可满足大部分需求。
缺点:
(1)加载反应慢;
(2)由于Java内存模型的原因偶尔会失败。(DCL失效)
(3)为避免失效,在JDK1.5之后,添加了volatile(不稳定),保证每次都是从主内存中读取;

private volatile static Singleton mInstance = null;



会损失一部分性能。但是这种优化,在《Java并非编程实践》中被指是丑陋的,不赞成使用。

4、静态内部类单例模式  (推荐使用)

public class Singleton{    private Singleton(){}    public static Singleton getInstance(){        return SingletonHolder.mInstance;    }    /**     *静态内部类     */    private static class SingletonHolder{        private static final Singleton mInstance = new Singleton();    }}


当第一次加载Singleton类时并不会初始化mInstance,只有在第一次调用Singleton的getInstance方法时才会导致mInstance被初始化。
因此,第一次调用getInstance方法会导致虚拟机加载SingletonHolder类,这种方式不仅能够确保线程安全,也能够保证单例对象的唯一性,同时也延迟了单例的实例化。推荐使用此法。

5、枚举单例

public enum SingletonEnum{    INSTANCE;    public void doSomething(){        //doSomething    }}

优点:默认枚举实例的创建是线程安全的,并且在任何情况下它都是一个单例。
why这么说呢,以上几种单例模式实现中,在一个情况下会出现创建对象的情况,那就是反序列化
通过序列化可以将一个单例的实例对象写到磁盘,然后在读回来,从而有效地获得一个实例。即使构造函数是私有的,反序列化时依然可以通过特殊的途径去创建类的一个新的实例,相当于调用该类的构造函数。
反序列化操作提供了一个很特别的钩子函数,类中具有一个私有的、被实例化的方法readResolve(),这个方法可以让开发人员控制对象的反序列化。例如,上述几个示例中如果要杜绝单例对象在被反序列化时重新生成对象,那么必须加入如下方法:
private Object readResolve() throws ObjectSreamException {    return mInstance;}


也就是在readResolve()方法中将mInstance对象返回,而不是默认的重新生成一个新的对象。而对于枚举,并不存在这个问题,因为即使反序列化它也不会重新生成新的实例。


6、使用容器实现单例模式

public class SingletonManager{    private static Map<String, Object> objMap = new HashMap<String, Object>();    private SingletonManager(){}    public static void registerService(String key, Object instance){        if( ! objMap.containsKey(key) ){            objMap.put(key, instance);        }    }    public static Objcet getService(String key){        return objMap.get(key);    }}


可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。

7、Android源码中的单例模式

在Android系统中,我们经常会通过Context获取系统级别的服务,如WindowsManagerService、ActivityManagerService等,更常用的是一个LayoutInflater的类,这些服务会在合适的时候以单例的形式注册在系统中,在需要的时候通过Context的getSystemService(String name)获取。
通常使用LayoutInflater.from(Context)来获取LayoutInflater服务,下面看看LayoutInflater.from(Context)的实现:
    /**     * Obtains the LayoutInflater from the given context.     */    public static LayoutInflater from(Context context) {        LayoutInflater LayoutInflater =                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);        if (LayoutInflater == null) {            throw new AssertionError("LayoutInflater not found.");        }        return LayoutInflater;    }
可以看出from(Context)函数内部调用的是Context类的getSystemService(String key)方法,跟踪到Context类可以看到,该类是抽象类:
public abstract class Context {     //代码省略}
那么Context对象的具体实现类是什么呢?其实在Application、Activity、Service中都会存在一个Context对象,即Context的总个数为Activity个数 + Service个数 + 1。下面我们就以Activity中的Context来分析。
我们知道,一个Activity的入口是ActivityThread的main函数,在main函数中创建一个新的ActivityThread对象,并且启动消息循环(UI线程),创建新的Activity、新的Context对象,然后将该Context对象传递给Activity。
    public static void main(String[] args) {        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");        SamplingProfilerIntegration.start();        // CloseGuard defaults to true and can be quite spammy.  We        // disable it here, but selectively enable it later (via        // StrictMode) on debug builds, but using DropBox, not logs.        CloseGuard.setEnabled(false);        Environment.initForCurrentUser();        // Set the reporter for event logging in libcore        EventLogger.setReporter(new EventLoggingReporter());        AndroidKeyStoreProvider.install();        // Make sure TrustedCertificateStore looks in the right place for CA certificates        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());        TrustedCertificateStore.setDefaultUserDirectory(configDir);        Process.setArgV0("<pre-initialized>");//主线程消息循环        Looper.prepareMainLooper();//创建ActivityThread对象        ActivityThread thread = new ActivityThread();        thread.attach(false);        if (sMainThreadHandler == null) {            sMainThreadHandler = thread.getHandler();        }        if (false) {            Looper.myLooper().setMessageLogging(new                    LogPrinter(Log.DEBUG, "ActivityThread"));        }        // End of event ActivityThreadMain.        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);        Looper.loop();        throw new RuntimeException("Main thread loop unexpectedly exited");    }
在main方法中,我们创建了一个ActivityThread对象后,调用了其attach函数,并且参数为false(非系统应用)。
 private void attach(boolean system) {        sCurrentActivityThread = this;        mSystemThread = system;        if (!system) {//不是系统应用            ViewRootImpl.addFirstDrawHandler(new Runnable() {                @Override                public void run() {                    ensureJitEnabled();                }            });            android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",                                                    UserHandle.myUserId());            RuntimeInit.setApplicationObject(mAppThread.asBinder());            final IActivityManager mgr = ActivityManagerNative.getDefault();            try {                mgr.attachApplication(mAppThread);//关联mAppThread            } catch (RemoteException ex) {                // Ignore            }            // Watch for getting close to heap limit.            BinderInternal.addGcWatcher(new Runnable() {                @Override public void run() {                    if (!mSomeActivitiesChanged) {                        return;                    }                    Runtime runtime = Runtime.getRuntime();                    long dalvikMax = runtime.maxMemory();                    long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();                    if (dalvikUsed > ((3*dalvikMax)/4)) {                        if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024)                                + " total=" + (runtime.totalMemory()/1024)                                + " used=" + (dalvikUsed/1024));                        mSomeActivitiesChanged = false;                        try {                            mgr.releaseSomeActivities(mAppThread);                        } catch (RemoteException e) {                        }                    }                }            });        } else {            // Don't set application object here -- if the system crashes,            // we can't display an alert, we just want to die die die.            android.ddm.DdmHandleAppName.setAppName("system_process",                    UserHandle.myUserId());            try {                mInstrumentation = new Instrumentation();                ContextImpl context = ContextImpl.createAppContext(                        this, getSystemContext().mPackageInfo);                mInitialApplication = context.mPackageInfo.makeApplication(true, null);                mInitialApplication.onCreate();            } catch (Exception e) {                throw new RuntimeException(                        "Unable to instantiate Application():" + e.toString(), e);            }        }        // add dropbox logging to libcore        DropBox.setReporter(new DropBoxReporter());        ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {            @Override            public void onConfigurationChanged(Configuration newConfig) {                synchronized (mResourcesManager) {                    // We need to apply this change to the resources                    // immediately, because upon returning the view                    // hierarchy will be informed about it.                    if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) {                        // This actually changed the resources!  Tell                        // everyone about it.                        if (mPendingConfiguration == null ||                                mPendingConfiguration.isOtherSeqNewer(newConfig)) {                            mPendingConfiguration = newConfig;                            sendMessage(H.CONFIGURATION_CHANGED, newConfig);                        }                    }                }            }            @Override            public void onLowMemory() {            }            @Override            public void onTrimMemory(int level) {            }        });    }

在attach函数中,参数为false的情况下,会通过Binder机制与ActivityManagerService通信,并且最终调用handleLaunchActivity函数,下面看下该函数的实现:
    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {        //代码省略        Activity a = performLaunchActivity(r, customIntent);//代码省略    }

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {        //省略代码        Activity activity = null;        try {            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();    //1、创建Activity            activity = mInstrumentation.newActivity(                    cl, component.getClassName(), r.intent);            StrictMode.incrementExpectedActivityCount(activity.getClass());            r.intent.setExtrasClassLoader(cl);            r.intent.prepareToEnterProcess();            if (r.state != null) {                r.state.setClassLoader(cl);            }        } catch (Exception e) {            if (!mInstrumentation.onException(activity, e)) {                throw new RuntimeException(                    "Unable to instantiate activity " + component                    + ": " + e.toString(), e);            }        }        try {    //创建Application对象            Application app = r.packageInfo.makeApplication(false, mInstrumentation);            if (localLOGV) Slog.v(TAG, "Performing launch of " + r);            if (localLOGV) Slog.v(                    TAG, r + ": app=" + app                    + ", appName=" + app.getPackageName()                    + ", pkg=" + r.packageInfo.getPackageName()                    + ", comp=" + r.intent.getComponent().toShortString()                    + ", dir=" + r.packageInfo.getAppDir());            if (activity != null) {        //2、创建Context对象                Context appContext = createBaseContextForActivity(r, activity);                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());                Configuration config = new Configuration(mCompatConfiguration);                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "                        + r.activityInfo.name + " with config " + config);//3、将appContext等对象attach到Activity中                activity.attach(appContext, this, getInstrumentation(), r.token,                        r.ident, app, r.intent, r.activityInfo, title, r.parent,                        r.embeddedID, r.lastNonConfigurationInstances, config,                        r.referrer, r.voiceInteractor);                if (customIntent != null) {                    activity.mIntent = customIntent;                }                r.lastNonConfigurationInstances = null;                activity.mStartedActivity = false;                int theme = r.activityInfo.getThemeResource();                if (theme != 0) {                    activity.setTheme(theme);                }                activity.mCalled = false;//4、调用Activity的onCreate方法                if (r.isPersistable()) {                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);                } else {                    mInstrumentation.callActivityOnCreate(activity, r.state);                }                //省略代码        } catch (SuperNotCalledException e) {            throw e;        } catch (Exception e) {            if (!mInstrumentation.onException(activity, e)) {                throw new RuntimeException(                    "Unable to start activity " + component                    + ": " + e.toString(), e);            }        }        return activity;    }

private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {        int displayId = Display.DEFAULT_DISPLAY;        try {            displayId = ActivityManagerNative.getDefault().getActivityDisplayId(r.token);        } catch (RemoteException e) {        }//5、创建Context对象,可以看到实现类是ContextImpl        ContextImpl appContext = ContextImpl.createActivityContext(                this, r.packageInfo, displayId, r.overrideConfig);        appContext.setOuterContext(activity);        Context baseContext = appContext;        //省略代码        return baseContext;    }

通过1~5处注释的代码分析,Context的实现类为ContextImpl。

class ContextImpl extends Context {    //代码省略    //ServiceFetcher通过getService获取服务对象    static class ServiceFetcher {                    int mContextCacheIndex = -1;         //获取系统服务       public Object getService(ContextImpl ctx) {                 ArrayList<Object> cache = ctx.mServiceCache;           Object service;           synchronized (cache) {                if (cache.size() == 0) {                          for (int i =0; i <sNextPerContextServiceCacheIndex; i++) {                        cache.add(null);                    }                } else {                    //从缓存中获取Service对象                    service =cache.get(mContextCacheIndex);                    if (service != null) {                        return service;                    }                }                service = createService(ctx);                cache.set(mContextCacheIndex,service);                return service;           }       }         /*       * 子类覆写该方法用来创建服务对象       */       public Object createService(ContextImpl ctx) {                throw new RuntimeException("Notimplemented");       }    }       //1、Service容器    private static final HashMap<String, ServiceFetcher> SYSTEM_SERVICE_MAP  =    new HashMap<String,ServiceFetcher>(); 2       private static int sNextPerContextServiceCacheIndex = 0;    //2、注册服务器    private static void registerService(String serviceName, ServiceFetcher fetcher) {                  if(!(fetcher instanceof StaticServiceFetcher)) {           fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;       }       SYSTEM_SERVICE_MAP.put(serviceName, fetcher);    }       //3、静态语句块,第一次加载该类时执行(只执行一次,保证实例的唯一性)    static {       //代码省略       //注册       registerService(LAYOUT_INFLATER_SERVERCE, new ServiceFetcher() {              public Object createService(ContextImpl ctx) {               return PolicyManager.makeNewLayoutInflate(ctx.getOuterContext());           }});              //代码省略    }      //4、根据key获取对应的服务   @Override    public Object getSystemService(String name) {         ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);       return fetcher == null ? null : fetcher.getService(this);    }   //代码省略}}

从ContextImpl类的部分代码中可以看到,在虚拟机第一次加载该类时会注册各种ServiceFetcher,其中就包含了LayoutInflater Service。将这些服务以键值对的形式存储在一个HashMap中,用户使用时只需根据Key来获取到对应的ServiceFetcher,然后通过ServiceFetcher对象的getService函数来获取具体的服务对象。当第一次获取是,会调用ServiceFetcher的createService函数创建服务对象,然后将该对象缓存到一个列表中,下次在取时直接从缓存中获取,避免重复创建对象,从而达到单例的效果。这种模式就是上面提到的使用容器实现的单例模式。




1 0
原创粉丝点击